qt/{qtwayland,qtdeclarative,qtsvg}: finish 6.11.0->6.11.1 vendored bump

Same half-done bump as qtbase: recipe tar= was 6.11.1 but the vendored source/
trees stayed 6.11.0. These three carry no Redox port (0 __redox__ markers), so
each is a clean pristine-6.11.1 re-lay:
 - qtwayland: pristine 6.11.1 (no patches)
 - qtdeclarative: pristine 6.11.1 + P1-skip-tools-crosscompile.patch
 - qtsvg: pristine 6.11.1; stale 6.11.0 source.tar replaced; CVE-2026-6210 patch
   dropped (the heap-overflow fix is already in upstream 6.11.1)
Drops the injected line-duplication corruption present in the old trees.
This commit is contained in:
2026-07-31 20:04:59 +03:00
parent 3a5b67ddd1
commit 9e692fc77f
662 changed files with 13732 additions and 4888 deletions
@@ -1,10 +1,11 @@
set(QT_REPO_MODULE_VERSION "6.11.0")
set(QT_REPO_MODULE_VERSION "6.11.1")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
set(QT_EXTRA_INTERNAL_TARGET_DEFINES
"QT_LEAN_HEADERS=1"
"QT_LEAN_HEADERS=2"
"QT_NO_QASCONST=1"
"QT_NO_FOREACH=1"
"QT_NO_QPAIR=1"
"QT_NO_QSNPRINTF=1"
"QT_NO_URL_CAST_FROM_STRING=1"
)
+1 -1
View File
@@ -1 +1 @@
a59ffcedd7cf65b8d2be249c9656f7d540a4a104
e50599b560e38d5991de59020671c01a42d1d40e
@@ -14,6 +14,24 @@ gccsetup --cc gcc --cxx g++ --config "$BAUHAUS_CONFIG"
cd "$CAFECC_BASEPATH"
BAUHAUS_IR_COMPRESSION=none COMPILE_ONLY=1 cmake -G Ninja -DAXIVION_ANALYSIS_TOOLCHAIN_FILE=/home/qt/bauhaus-suite/profiles/cmake/axivion-launcher-toolchain.cmake -DCMAKE_PREFIX_PATH=/home/qt/work/qt/qtdeclarative/build -DCMAKE_PROJECT_INCLUDE_BEFORE=/home/qt/bauhaus-suite/profiles/cmake/axivion-before-project-hook.cmake -B build -S . --fresh
cmake --build build -j4
ROOT_DIR=src
MAGIC="Qt-Security score:critical"
INCLUDE_FILES=""
while IFS= read -r file; do
if [ -z "$INCLUDE_FILES" ]; then
INCLUDE_FILES="$file"
else
INCLUDE_FILES="$INCLUDE_FILES:$file"
fi
done <<EOF
$(grep -rl "$MAGIC" "$ROOT_DIR")
EOF
export INCLUDE_FILES
for MODULE in qtqml qtquick qtquickcontrols qtquickdialogs qtquicklayouts qtquicktest; do
export MODULE
export PLUGINS=""
@@ -1,16 +1,16 @@
dependencies:
../qtbase:
ref: 8ba7ea4b77a4b8f1948760221e264917ddc9e1c8
ref: 59c81a3c2247b821b9b84b4eb8d939b77e07e276
required: true
../qtimageformats:
ref: ade536558e868143d615512a6873daabb8e9195b
ref: 77f35e5694885844c3eb3a737769b5ed290b8ccc
required: false
../qtlanguageserver:
ref: fa86fa006217f26d742127a6ef96677c87315b5e
ref: 7ef3bccd89cc163a664b46fe80a4bd56084b1983
required: false
../qtshadertools:
ref: 1c7a78fd7480f11a49eb8135b44d138edb0f0df8
ref: b3b8537b3c5ab01919e30b87d48d2f1ba1f2a3fe
required: false
../qtsvg:
ref: 4126867385dc0b7a9404dcd69726c28ed0d5e10a
ref: 2596f43da2dc72d2afecc084355b0f5f87922a6c
required: false
@@ -5,7 +5,7 @@ plugins {
QtBuild {
// Relative for Qt (Installer or MaintenanceTool) installations.
qtPath = file('../../../../../../../6.11.0')
qtPath = file('../../../../../../../6.11.1')
projectPath = file('../../qtabstractitemmodel')
}
@@ -1,5 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.10.1' apply false
id 'com.android.application' version '9.0.0' apply false
}
@@ -7,7 +7,7 @@ plugins {
//! [build.gradle QtBuild config]
QtBuild {
// Relative for Qt (Installer or MaintenanceTool) installations.
qtPath = file("../../../../../../../6.11.0")
qtPath = file("../../../../../../../6.11.1")
projectPath = file("../../qtabstractlistmodel")
}
//! [build.gradle QtBuild config]
@@ -5,7 +5,7 @@ plugins {
QtBuild {
// Relative for Qt (Installer or MaintenanceTool) installations.
qtPath = file('../../../../../../6.11.0')
qtPath = file('../../../../../../6.11.1')
projectPath = file('../../qtquickview')
}
@@ -1,4 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.10.1' apply false
id 'com.android.application' version '9.0.0' apply false
}
@@ -6,7 +6,7 @@ plugins {
QtBuild {
// Relative for Qt (Installer or MaintenanceTool) installations.
qtPath = file("../../../../../../6.11.0")
qtPath = file("../../../../../../6.11.1")
projectPath = file("../../qtquickview")
}
@@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.10.1" apply false
id("com.android.application") version "9.0.0" apply false
id("org.jetbrains.kotlin.android") version "2.3.0" apply false
}
@@ -16,6 +16,8 @@ qt_add_executable(animationexample
main.cpp
)
set_target_properties(animationexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(animationexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -16,6 +16,8 @@ qt_add_executable(draganddropexample
main.cpp
)
set_target_properties(draganddropexample PROPERTIES FOLDER "qtc_runnable")
qt_add_qml_module(draganddropexample
URI draganddrop
QML_FILES
@@ -7,6 +7,7 @@ import QtQuick.Layouts
Control {
padding: 8
width: 320; height: 240
contentItem: ColumnLayout {
component DragAndDropArea: Rectangle {
id: item
@@ -6,6 +6,8 @@
\examplecategory {Data Processing & I/O}
\brief A collection of QML local storage examples.
\borderedimage qml-localstorage-example.png
{Activity tracker with trip entries showing dates,
descriptions, and distances}
\e{Local Storage} is a collection of small QML examples relating to
Qt Quick's \l{local storage} functionality.
@@ -5,7 +5,6 @@
#define SONGDATAGENERATOR_H
#include "mediaelement.h"
#include <QPair>
#include <QStringList>
class SongDataGenerator
@@ -14,6 +14,8 @@ qt_add_executable(itemswitcherexample WIN32 MACOSX_BUNDLE
main.cpp
)
set_target_properties(itemswitcherexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(itemswitcherexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -14,6 +14,8 @@ qt_add_executable(neumorphicpanelexample WIN32 MACOSX_BUNDLE
main.cpp
)
set_target_properties(neumorphicpanelexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(neumorphicpanelexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -14,6 +14,8 @@ qt_add_executable(testbedexample WIN32 MACOSX_BUNDLE
main.cpp
)
set_target_properties(testbedexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(testbedexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -14,6 +14,8 @@ qt_add_executable(affectorsexample WIN32 MACOSX_BUNDLE
main.cpp
)
set_target_properties(affectorsexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(affectorsexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -14,6 +14,8 @@ qt_add_executable(emittersexample WIN32 MACOSX_BUNDLE
main.cpp
)
set_target_properties(emittersexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(emittersexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -14,6 +14,8 @@ qt_add_executable(imageparticleexample WIN32 MACOSX_BUNDLE
main.cpp
)
set_target_properties(imageparticleexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(imageparticleexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -14,6 +14,8 @@ qt_add_executable(systemexample WIN32 MACOSX_BUNDLE
main.cpp
)
set_target_properties(systemexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(systemexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -12,6 +12,8 @@ add_subdirectory("../shared" "shared")
qt_add_executable(pointerhandlersexample WIN32 MACOSX_BUNDLE main.cpp)
set_target_properties(pointerhandlersexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(pointerhandlersexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -16,6 +16,8 @@ qt_add_executable(shapesexample
main.cpp
)
set_target_properties(shapesexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(shapesexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -16,6 +16,8 @@ qt_add_executable(weatherforecastexample
main.cpp
)
set_target_properties(weatherforecastexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(weatherforecastexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -108,7 +108,8 @@ void MainWindow::createQuickWidgetsInTabs(QMdiArea *mdiArea)
widget->setSource(QUrl("qrc:quickwidget/rotatingsquare.qml"));
widget->setWindowTitle(QString::asprintf("Tab %d", i + 1));
QPushButton *btn = new QPushButton(msgToTopLevel, widget);
connect(btn, &QPushButton::clicked, widget, [=] {
connect(btn, &QPushButton::clicked,
widget, [this, widget, btn, tabWidget, msgFromTopLevel, msgToTopLevel] {
if (widget->parent()) {
widget->setAttribute(Qt::WA_DeleteOnClose, true);
widget->setParent(nullptr);
@@ -167,6 +167,15 @@ void QuickRenderer::ensureTexture()
qreal dpr = m_quickWindow->devicePixelRatio();
QSize textureSize = m_quickWindow->size() * dpr;
QOpenGLFunctions *f = m_context->functions();
if (m_textureId && m_textureSize != textureSize) {
f->glDeleteTextures(1, &m_textureId);
m_textureId = 0;
}
if (m_textureId)
return;
f->glGenTextures(1, &m_textureId);
f->glBindTexture(GL_TEXTURE_2D, m_textureId);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -174,6 +183,7 @@ void QuickRenderer::ensureTexture()
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.width(), textureSize.height(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
m_quickWindow->setRenderTarget(QQuickRenderTarget::fromOpenGLTexture(m_textureId, textureSize));
m_textureSize = textureSize;
}
void QuickRenderer::render(QMutexLocker<QMutex> *lock)
@@ -62,6 +62,7 @@ private:
QQuickRenderControl *m_renderControl;
QRhi *m_rhi;
uint m_textureId;
QSize m_textureSize;
CubeRenderer *m_cubeRenderer;
QMutex m_quitMutex;
bool m_quit;
@@ -16,6 +16,8 @@ qt_add_executable(textexample
main.cpp
)
set_target_properties(textexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(textexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -28,16 +28,19 @@ Rectangle {
font.pixelSize: screen.pixelSize
color: screen.textColor
text: screen.text
renderType: Text.CurveRendering
}
Text {
font.pixelSize: screen.pixelSize
color: screen.textColor
text: screen.text
renderType: Text.CurveRendering
}
Text {
font.pixelSize: screen.pixelSize
color: screen.textColor
text: screen.text
renderType: Text.CurveRendering
}
}
}
@@ -18,6 +18,8 @@ qt_add_executable(viewsexample
main.cpp
)
set_target_properties(viewsexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(viewsexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -16,6 +16,8 @@ qt_add_executable(windowexample
main.cpp
)
set_target_properties(windowexample PROPERTIES FOLDER "qtc_runnable")
target_link_libraries(windowexample PRIVATE
Qt6::Core
Qt6::Gui
@@ -34,16 +34,21 @@ Dialog {
focus: true
modal: true
title: qsTr("Add Contact")
standardButtons: Dialog.Ok | Dialog.Cancel
contentItem: ContactForm {
id: form
Binding {
target: dialog.standardButton(Dialog.Ok)
property: "enabled"
value: form.fullName.text
&& form.address.text
&& form.city.text
&& form.number.text
}
}
onAccepted: {
if (form.fullName.text && form.address.text && form.city.text && form.number.text) {
finished(form.fullName.text, form.address.text, form.city.text, form.number.text);
}
finished(form.fullName.text, form.address.text, form.city.text, form.number.text);
}
}
@@ -68,5 +68,6 @@ GridLayout {
Layout.minimumWidth: grid.minimumInputSize
Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
placeholderText: grid.placeholderText
inputMethodHints: Qt.ImhDigitsOnly
}
}
@@ -18,9 +18,9 @@ ApplicationWindow {
id: contactDialog
onFinished: function(fullName, address, city, number) {
if (window.currentContact === -1)
contactView.model.append(fullName, address, city, number)
contactView.model.add(fullName, address, city, number)
else
contactView.model.set(window.currentContact, fullName, address, city, number)
contactView.model.edit(window.currentContact, fullName, address, city, number)
}
}
@@ -53,23 +53,20 @@ QVariantMap ContactModel::get(int row) const
return { {"fullName", contact.fullName}, {"address", contact.address}, {"city", contact.city}, {"number", contact.number} };
}
void ContactModel::append(const QString &fullName, const QString &address, const QString &city, const QString &number)
void ContactModel::add(const QString &fullName, const QString &address, const QString &city, const QString &number)
{
int row = 0;
while (row < m_contacts.count() && fullName > m_contacts.at(row).fullName)
while (row < m_contacts.count() && (fullName.compare(m_contacts.at(row).fullName, Qt::CaseInsensitive) > 0))
++row;
beginInsertRows(QModelIndex(), row, row);
m_contacts.insert(row, {fullName, address, city, number});
endInsertRows();
}
void ContactModel::set(int row, const QString &fullName, const QString &address, const QString &city, const QString &number)
void ContactModel::edit(int row, const QString &fullName, const QString &address, const QString &city, const QString &number)
{
if (row < 0 || row >= m_contacts.count())
return;
m_contacts.replace(row, { fullName, address, city, number });
dataChanged(index(row, 0), index(row, 0), { FullNameRole, AddressRole, CityRole, NumberRole });
remove(row);
add(fullName, address, city, number);
}
void ContactModel::remove(int row)
@@ -28,8 +28,8 @@ public:
QHash<int, QByteArray> roleNames() const;
Q_INVOKABLE QVariantMap get(int row) const;
Q_INVOKABLE void append(const QString &fullName, const QString &address, const QString &city, const QString &number);
Q_INVOKABLE void set(int row, const QString &fullName, const QString &address, const QString &city, const QString &number);
Q_INVOKABLE void add(const QString &fullName, const QString &address, const QString &city, const QString &number);
Q_INVOKABLE void edit(int row, const QString &fullName, const QString &address, const QString &city, const QString &number);
Q_INVOKABLE void remove(int row);
private:
@@ -6,13 +6,15 @@ project(StyleKitExample VERSION 1.0 LANGUAGES CXX)
find_package(Qt6 6.11 REQUIRED Core Gui Qml Quick QuickControls2)
find_program(QSB_EXECUTABLE qsb HINTS "${QT_HOST_PATH}/bin")
if(NOT QSB_EXECUTABLE OR NOT EXISTS "${QSB_EXECUTABLE}")
message(FATAL_ERROR "qsb not found. Please install Qt Shader Tools to build this example.")
endif()
qt_standard_project_setup(REQUIRES 6.11)
set(STYLES
styles/Plain.qml
styles/Haze.qml
styles/Experimental.qml
styles/Vitrum.qml
styles/CustomDelegates.qml
)
@@ -1,496 +0,0 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import Qt.labs.StyleKit
Style {
id: style
control {
leftPadding: 10
topPadding: 5
rightPadding: 10
bottomPadding: 5
background {
implicitWidth: 100
implicitHeight: 40
shadow {
opacity: 0.8
scale: 1.1
verticalOffset: 5
horizontalOffset: 2
}
}
handle {
implicitWidth: 25
implicitHeight: 25
radius: 25
color: "lightslategray"
}
indicator {
foreground.margins: 2
// first.color: "green"
// second.color: "yellow"
// up.color: "red" // alias. Can control.indicator.up have control.indicator as alt?
// down.color: "blue"
}
text.color: "blue"
transition: Transition {
StyleAnimation {
animateColors: true
animateBackgroundBorder: true
duration: 300
}
}
hovered {
transition: null
background {
border.width: 4
}
handle {
border.width: 2
}
text.color: "yellow"
}
pressed {
background {
scale: 0.95
}
}
}
pane {
background {
border.width: 0
implicitWidth: 200
implicitHeight: 200
shadow.visible: false
}
}
frame {
background {
border.width: 1
shadow.visible: true
}
}
button {
background {
radius: 8
opacity: 0.8
gradient: Gradient {
GradientStop { position: 0.0; color: Qt.alpha("black", 0.0)}
GradientStop { position: 1.0; color: Qt.alpha("black", 0.4)}
}
}
}
radioButton {
indicator {
foreground {
margins: 4
radius: 25 / 2
border.width: 0
}
}
}
textInput {
background {
implicitWidth: 200
}
}
spinBox {
padding: 0
background {
implicitWidth: 100
}
}
comboBox {
background {
implicitWidth: 200
implicitHeight: 30
gradient: null
}
indicator {
foreground.scale: 0.5
}
pressed.background.scale: 1.0
variations: [contextMenu, mini, alert, "dont crash"]
}
StyleVariation {
id: contextMenu
itemDelegate {
background {
radius: 0
border.width: 0
shadow.color: "transparent"
gradient: null
}
hovered.background.color: "pink"
}
control.transition: null
}
StyleVariation {
id: mini
control.background.implicitHeight: 10
}
StyleVariation {
id: alert
control.background.color: "red"
}
slider {
background.implicitWidth: 180
indicator {
implicitHeight: 8
radius: 8
foreground {
radius: 8
}
}
handle {
radius: 25
first {
topRightRadius: 0
bottomRightRadius: 0
color: "orange"
}
second {
topLeftRadius: 0
bottomLeftRadius: 0
color: "yellow"
}
}
vertical {
handle {
first {
radius: 25
topLeftRadius: 0
bottomLeftRadius: 0
}
second {
radius: 25
topRightRadius: 0
bottomRightRadius: 0
}
}
}
}
switchControl {
indicator {
implicitWidth: 60
implicitHeight: 30
radius: 5
foreground.radius: 4
}
handle {
leftMargin: 3
rightMargin: 3
}
}
itemDelegate {
background {
radius: 0
border.width: 0
shadow.visible: false
gradient: null
}
transition: null
hovered {
background.color: style.palette.highlight
}
}
// THEMES
light: Theme {
control {
background {
border.color: "lightgray"
shadow.color: "#808080"
}
checked {
background.shadow.color: "white"
background.color: "blue"
}
focused {
background.border.color: "white"
background.shadow.color: "white"
}
hovered {
background {
border.color: "white"
shadow.color: "white"
}
}
disabled {
background {
color: "#a7a7a7"
shadow.color: "transparent"
}
}
}
button {
background.color: "lightgray"
}
textField {
variations: StyleVariation {
button {
background {
radius: 0
implicitWidth: 50
implicitHeight: 20
margins: 4
color: "orange"
shadow.color: "transparent"
}
}
}
}
palettes {
system.window: "#989898"
textField.text: "#4e4e4e"
button {
buttonText: "white"
highlightedText: "white"
brightText: "#4e4e4e"
disabled.buttonText: "#4e4e4e"
disabled.highlightedText: "#4e4e4e"
}
}
}
dark: Theme {
control {
background {
border.color: "#3d373b"
shadow.color: "#404040"
color: "#8e848a"
}
handle {
border.color: "black"
}
focused {
background {
border.color: "white"
shadow.color: "white"
color: "#bbbbbb"
}
}
hovered {
background {
border.color: "white"
shadow.color: "white"
}
}
disabled {
background {
color: "red"//"#766e73"
shadow.color: "transparent"
}
checked.background.color: "green"
}
}
textField {
variations: StyleVariation {
button {
background {
radius: 0
implicitWidth: 50
implicitHeight: 20
margins: 4
color: "lightblue"
shadow.color: "transparent"
}
}
}
}
palettes {
system.window: "#544e52"
textField.text: "black"
button {
buttonText: "white"
highlightedText: "white"
brightText: "white"
disabled.buttonText: "darkgray"
disabled.highlightedText: "darkgray"
}
}
}
CustomTheme {
name: "HighContrast"
theme: Theme {
control {
background {
border.color: "black"
shadow.color: "transparent"
color: "white"
gradient: null
}
indicator {
implicitWidth: 30
implicitHeight: 30
}
handle {
border.color: "black"
implicitWidth: 30
implicitHeight: 30
radius: 30
}
hovered {
background {
border.color: "red"
border.width: 3
}
indicator {
border.color: "red"
border.width: 3
}
}
checked {
background {
border.color: "red"
border.width: 3
}
}
disabled {
background.color: "gray"
}
}
slider {
indicator {
implicitWidth: 180
implicitHeight: 8
}
}
switchControl {
spacing: 2
indicator {
implicitWidth: 60
implicitHeight: 34
}
}
palettes {
system.window: "white"
textField.text: "black"
button {
buttonText: "black"
highlightedText: "black"
brightText: "black"
disabled.buttonText: "white"
disabled.highlightedText: "white"
}
}
}
}
CustomTheme {
name: "Green"
theme: Theme {
control {
background {
border.color: "lightgray"
shadow.color: "#404040"
color: "#a0c0a0"
}
handle {
border.color: "lightgray"
shadow.color: "#404040"
color: "#a0c0a0"
}
indicator {
foreground.color: "lime"
foreground.image.color: "black"
}
hovered {
background {
border.color: "lightgreen"
shadow.color: "lightgreen"
color: "lightgreen"
}
handle {
border.color: "lightgreen"
shadow.color: "lightgreen"
color: "lightgreen"
}
}
checked {
background {
shadow.color: "lightgreen"
color: "lightgreen"
}
}
disabled {
background {
color: "#80a080"
shadow.color: "transparent"
}
}
}
palettes {
system.window: "#547454"
textField.text: "green"
checkBox.buttonText: "white"
button {
buttonText: "black"
highlightedText: "white"
disabled.buttonText: "lightgray"
disabled.highlightedText: "lightgray"
}
}
}
}
CustomTheme {
name: "Empty"
theme: Theme {}
}
}
@@ -256,15 +256,6 @@ Style {
}
}
StyleVariation {
name: "alert"
abstractButton.background {
color: "orchid"
border.color: "orchid"
shadow.color: "orchid"
}
}
/* You can also set one or more StyleVariations on a control type. Unlike Instance
* variations—which apply only to specific control instances—type variations are applied
* to *all* instances of a control type without requiring the application to use attached
@@ -413,6 +404,15 @@ Style {
}
}
StyleVariation {
name: "alert"
abstractButton.background {
color: "orchid"
border.color: "orchid"
shadow.color: "orchid"
}
}
CustomControl {
controlType: fancyButton
background {
@@ -513,7 +513,6 @@ Style {
}
scrollBar {
background.color: "#8e848a"
indicator.foreground.color: "white"
}
@@ -534,6 +533,15 @@ Style {
background.shadow.visible: false
}
StyleVariation {
name: "alert"
abstractButton.background {
color: "orchid"
border.color: "orchid"
shadow.color: "orchid"
}
}
CustomControl {
controlType: fancyButton
background {
@@ -690,8 +698,6 @@ Style {
background.implicitHeight: 15
indicator.implicitWidth: 15
indicator.implicitHeight: 15
background.color: "#8e848a"
background.border.width: 1
indicator.border.width: 3
indicator.foreground.margins: 3
indicator.foreground.color: "lightgray"
@@ -234,6 +234,7 @@ Style {
scrollBar {
padding: 2
background.implicitHeight: 20
background.visible: false
indicator.implicitHeight: 20
vertical {
background.implicitWidth: 20
@@ -5,4 +5,5 @@ qt_commandline_subconfig(src/qml)
qt_commandline_subconfig(src/qmlmodels)
qt_commandline_subconfig(src/quick)
qt_commandline_subconfig(src/quickcontrols)
qt_commandline_subconfig(src/quickshapes)
qt_commandline_subconfig(src/quicktemplates)
@@ -6,6 +6,9 @@
#include <QString>
#include <wtf/ASCIICType.h>
#include <wtf/unicode/Unicode.h>
#include <QtCore/qhashfunctions.h>
#include <memory>
namespace WTF {
@@ -27,6 +30,15 @@ public:
bool operator!() const { return isEmpty(); }
void dump(PrintStream &) const {}
private:
friend size_t qHash(const String &key, size_t seed = 0) noexcept
{
// delegate to qHash(QString)
const QString &s = key;
using QT_PREPEND_NAMESPACE(qHash);
return qHash(s, seed);
}
};
template <>
@@ -397,36 +397,6 @@ template <typename ResultType> struct ArithmeticOperations<unsigned, int, Result
}
};
template <typename ResultType> struct ArithmeticOperations<unsigned, long, ResultType, false, true> {
static inline bool add(unsigned lhs, long rhs, ResultType& result) WARN_UNUSED_RETURN
{
int64_t temp = static_cast<int64_t>(lhs) + static_cast<int64_t>(rhs);
if (temp < std::numeric_limits<ResultType>::min()) return false;
if (temp > std::numeric_limits<ResultType>::max()) return false;
result = static_cast<ResultType>(temp);
return true;
}
static inline bool sub(unsigned lhs, long rhs, ResultType& result) WARN_UNUSED_RETURN
{
int64_t temp = static_cast<int64_t>(lhs) - static_cast<int64_t>(rhs);
if (temp < std::numeric_limits<ResultType>::min()) return false;
if (temp > std::numeric_limits<ResultType>::max()) return false;
result = static_cast<ResultType>(temp);
return true;
}
static inline bool multiply(unsigned lhs, long rhs, ResultType& result) WARN_UNUSED_RETURN
{
int64_t temp = static_cast<int64_t>(lhs) * static_cast<int64_t>(rhs);
if (temp < std::numeric_limits<ResultType>::min()) return false;
if (temp > std::numeric_limits<ResultType>::max()) return false;
result = static_cast<ResultType>(temp);
return true;
}
static inline bool equals(unsigned lhs, long rhs)
{
return static_cast<int64_t>(lhs) == static_cast<int64_t>(rhs);
}
};
template <typename U, typename V, typename R> static inline bool safeAdd(U lhs, V rhs, R& result)
{
return ArithmeticOperations<U, V, R>::add(lhs, rhs, result);
@@ -34,7 +34,7 @@ if(QT_FEATURE_qml_xmllistmodel)
add_subdirectory(qmlxmllistmodel)
endif()
if(TARGET Qt::Gui AND QT_FEATURE_qml_animation)
if(TARGET Qt::Gui AND TARGET Qt::qsb AND QT_FEATURE_qml_animation)
add_subdirectory(quick)
add_subdirectory(quicklayouts)
@@ -117,7 +117,7 @@ if(QT_FEATURE_qml_network)
endif()
endif()
if(TARGET Qt::Gui AND QT_FEATURE_qml_animation)
if(TARGET Qt::Gui AND TARGET Qt::qsb AND QT_FEATURE_qml_animation)
find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Test)
if(QT_FEATURE_testlib AND TARGET Qt::Test)
add_subdirectory(qmltest)
@@ -26,9 +26,9 @@
//! [status-docs]
//! [request-docs]
\qmlmethod \1::request()
\qmlmethod void \1::request()
Requests the permission with its current properties.
\sa QCoreApplication::requestPermission
\sa QCoreApplication::requestPermission()
//! [request-docs]
@@ -67,6 +67,7 @@ Q_STATIC_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects")
\table 70%
\row
\li \image multieffect-example1.png
{Qt logo in grayscale showing full desaturation effect}
\li \qml
import QtQuick
import QtQuick.Effects
@@ -97,6 +98,7 @@ Q_STATIC_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects")
\table 70%
\row
\li \image multieffect-example1.png
{Qt logo in grayscale showing full desaturation effect}
\li \qml
import QtQuick
import QtQuick.Effects
@@ -120,6 +122,8 @@ Q_STATIC_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects")
\table 70%
\row
\li \image multieffect-example2.png
{Qt logo with green glow showing brightness, saturation, and
blur effects combined}
\li \qml
import QtQuick
import QtQuick.Effects
@@ -145,6 +149,8 @@ Q_STATIC_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects")
\table 70%
\row
\li \image multieffect-example3.png
{Four stages of Qt logo fading with mask and colorization,
from visible green to dissolved pink particles}
\li \qml
import QtQuick
import QtQuick.Effects
@@ -250,6 +256,8 @@ void QQuickMultiEffect::setSource(QQuickItem *item)
Note that \l paddingRect is always added to the size.
\image multieffect-example4.png
{Blurred images comparing autoPaddingEnabled true with extra
space versus false with clipped edges}
\sa paddingRect
@@ -282,6 +290,8 @@ void QQuickMultiEffect::setAutoPaddingEnabled(bool enabled)
false so that the shadow fits inside the MultiEffect item.
\image multieffect-example5.png
{Two Qt logos with shadows comparing paddingRect values, showing
clipped shadow at zero versus full shadow with padding}
\sa autoPaddingEnabled
@@ -26,6 +26,7 @@ QT_BEGIN_NAMESPACE
\table 70%
\row
\li \image rectangularshadow-example-1.png
{Rounded rectangle with soft shadow offset to upper left}
\li \qml
import QtQuick
import QtQuick.Effects
@@ -96,23 +97,31 @@ QT_BEGIN_NAMESPACE
\li offset: (0, 0) \br
blur: 20 \br
spread: 0 \br
\li \image rectangularshadow-css-1.png
\li \image rectangularshadow-item-1.png
\li \image rectangularshadow-css-1.png {CSS box-shadow with blur 20}
\li \image rectangularshadow-item-1.png {RectangularShadow with blur 20}
\li \image rectangularshadow-itemblur-1.png
{RectangularShadow with blur 24 for closer CSS match}
\row
\li offset: (-10, -20) \br
blur: 40 \br
spread: 0 \br
\li \image rectangularshadow-css-2.png
{CSS box-shadow with offset and blur 40}
\li \image rectangularshadow-item-2.png
{RectangularShadow with offset and blur 40}
\li \image rectangularshadow-itemblur-2.png
{RectangularShadow with blur 48 for closer CSS match}
\row
\li offset: (-10, -20) \br
blur: 40 \br
spread: 10 \br
\li \image rectangularshadow-css-3.png
{CSS box-shadow with offset, blur 40, and spread 10}
\li \image rectangularshadow-item-3.png
{RectangularShadow with offset, blur 40, and spread 10}
\li \image rectangularshadow-itemblur-3.png
{RectangularShadow with blur 48 and spread 10 for closer CSS
match}
\endtable
@@ -128,6 +128,33 @@ Q_STATIC_LOGGING_CATEGORY(lcTableModel, "qt.qml.tablemodel")
\l [QtQuickControls2]{TextField}, and so that delegate is declared
last as a fallback.
\section1 Using the TableModel with External JSON Sources
If the structure of the data in a JSON object matches the structure defined
by the TableModel, it can be assigned directly to the model using the rows
property. All rows in the data must conform to the columns defined by the model.
For example, if the model is defined as:
\snippet main.qml model
The source has the required data but not necessarily in the
required structure, you can download and parse it into a form that
conforms to the structure that the model defines and assign the result directly.
\snippet main.qml parse
and the result can be assigned directly
\snippet main.qml assign
You can also provide the JSON object by importing a JavaScript file as a
module. See the documentation for \l TreeModel for an example on how to do this.
\warning Ensure that the JSON data comes from a trusted source. Since the
model dynamically populates its rows based on the input, malformed or
untrusted JSON can lead to unexpected behavior or performance issues.
\sa TableModelColumn, TableView, QAbstractTableModel
*/
@@ -56,6 +56,42 @@ static const QString ROWS_PROPERTY_NAME = u"rows"_s;
To add new rows, use \l appendRow(). To modify existing rows, use
\l setRow(), \l removeRow() and \l clear().
\section1 Using the TreeModel with External JSON Sources
While the intended use of the TreeModel is to define the JSON data in-place in
the QML file, you can use any JSON object with the model as long as
its structure matches the structure defined by the TreeModel. If all the rows
of the JSON object conform to the columns defined by the model, you can
assign it directly through the rows property.
The source of the JSON object can be a file. For example, if the model is
defined as
\snippet setRowsViaJSON.qml model
and the JSON object stored in the file and looks like
\snippet TreeData.js file
then this object can be provided by importing the JavaScript file as a
module
\snippet setRowsViaJSON.qml import
and then it can be assigned directly
\snippet setRowsViaJSON.qml assignment
\warning Ensure that the JSON data comes from a trusted source. Since the
model dynamically populates its rows based on the input, malformed or
untrusted JSON can lead to unexpected behavior or performance issues.
See \l TableModel for an another example where a JSON object is parsed
into and assigned as a JavaScript array.
\sa TableModelColumn, TreeView
*/
QQmlTreeModel::QQmlTreeModel(QObject *parent)
@@ -122,12 +158,21 @@ void QQmlTreeModel::setRowsPrivate(const QVariantList &rowsAsVariantList)
beginResetModel();
// In case the model is empty and we cannot insert any new rows in the loop below,
// we don't want to emit rowsChanged
const bool wasEmpty = mRows.empty();
// We don't clear the column or role data, because a TreeModel should not be reused in that way.
// Once it has valid data, its columns and roles are fixed.
mRows.clear();
for (const auto &rowAsVariant : rowsAsVariantList)
mRows.push_back(std::make_unique<QQmlTreeRow>(rowAsVariant));
for (const auto &rowAsVariant : rowsAsVariantList) {
if (rowAsVariant.canConvert<QVariantMap>())
mRows.push_back(std::make_unique<QQmlTreeRow>(rowAsVariant));
else
qmlWarning(this) << "Cannot create tree row as the row does not contain "
<< "key-value pairs";
}
// Gather metadata the first time rows is set.
// If we call setrows on an empty model, mInitialRows will be empty, but mRows is not
@@ -135,7 +180,14 @@ void QQmlTreeModel::setRowsPrivate(const QVariantList &rowsAsVariantList)
fetchColumnMetadata();
endResetModel();
emit rowsChanged();
// was empty, still empty => no emit
// was empty, now non-empty => emit
// was not empty, now empty => emit
// was not empty, now non-empty => emit (there was a clear in-between)
if (!wasEmpty || !mRows.empty())
emit rowsChanged();
}
QVariant QQmlTreeModel::dataPrivate(const QModelIndex &index, const QString &roleName) const
@@ -212,17 +264,23 @@ void QQmlTreeModel::appendRow(QModelIndex parent, const QVariant &row)
qmlWarning(this) << "append: could not find any node at the specified index"
<< " - the new row will be appended to root";
beginInsertRows(QModelIndex(),
if (data.canConvert<QVariantMap>()) {
beginInsertRows(QModelIndex(),
static_cast<int>(mRows.size()),
static_cast<int>(mRows.size()));
mRows.push_back(std::make_unique<QQmlTreeRow>(data));
mRows.push_back(std::make_unique<QQmlTreeRow>(data));
// Gather metadata the first time a row is added.
if (mColumnMetadata.isEmpty())
fetchColumnMetadata();
// Gather metadata the first time a row is added.
if (mColumnMetadata.isEmpty())
fetchColumnMetadata();
endInsertRows();
endInsertRows();
} else {
qmlWarning(this) << "Cannot create tree row as the row does not contain "
<< "key-value pairs";
return;
}
}
emit rowsChanged();
@@ -415,6 +473,7 @@ QModelIndex QQmlTreeModel::index(int row, int column, const QModelIndex &parent)
\table
\row \li \inlineimage treemodel.svg
{Tree diagram showing nodes A through F with index paths}
\li \list
\li The root of the tree is special, as it can be referenced by an invalid
@@ -20,7 +20,7 @@ QQmlTreeRow::QQmlTreeRow(QQmlTreeRow *parentItem)
QQmlTreeRow::QQmlTreeRow(const QVariant &data, QQmlTreeRow *parentItem)
: m_parent(parentItem)
{
QVariantMap variantMap = data.toJsonObject().toVariantMap();
QVariantMap variantMap = data.toMap();
unpackVariantMap(variantMap);
}
@@ -30,6 +30,7 @@ QT_BEGIN_NAMESPACE
The MenuItem type provides a QML API for native platform menu items.
\image qtlabsplatform-menu.png
{Native menu with Zoom In and Zoom Out items}
A menu item consists of an \l icon, \l text, and \l shortcut.
@@ -18,6 +18,7 @@ QT_BEGIN_NAMESPACE
that has the \l {MenuItem::}{separator} property set to \c true by default.
\image qtlabsplatform-menubar.png
{Native menubar with Edit menu showing Cut, Copy, and Paste}
\labs
@@ -10,6 +10,7 @@ qt_internal_add_qml_module(LabsStyleKit
IMPORTS QtQuick.Controls.Basic
SOURCES
qqstylekit_p.h qqstylekit.cpp
qqstylekitanimation.cpp
qqstylekitcontrol_p.h qqstylekitcontrol.cpp
qqstylekitcontrols_p.h qqstylekitcontrols.cpp
qqstylekitcontrolstate_p.h qqstylekitcontrolstate.cpp
@@ -37,72 +37,102 @@ T.ProgressBar {
palette: control.palette
}
contentItem: IndicatorDelegate {
quickControl: control
indicatorStyle: styleReader.indicator
secondProgress: control.visualPosition
StyleKitLayout {
id: progressBarLayout
container: control.contentItem
layoutItems: [
StyleKitLayoutItem {
id: indicatorItem
item: indicator
margins {
left: styleReader.indicator.leftMargin
right: styleReader.indicator.rightMargin
top: styleReader.indicator.topMargin
bottom: styleReader.indicator.bottomMargin
}
alignment: styleReader.indicator.alignment
fillWidth: styleReader.indicator.implicitWidth === Style.Stretch
fillHeight: styleReader.indicator.implicitHeight === Style.Stretch
}
]
}
Connections {
target: control
function onIndeterminateChanged() {
if (indeterminate) {
control.contentItem.firstProgress = 0
control.contentItem.secondProgress = 0
control.contentItem.indeterminateAnim.restart()
} else {
control.contentItem.indeterminateAnim.stop()
control.contentItem.firstProgress = 0
control.contentItem.secondProgress = Qt.binding(()=>{ return control.visualPosition })
contentItem: Item {
implicitWidth: Math.max(indicator.implicitWidth, progressBarLayout.implicitWidth)
implicitHeight: Math.max(indicator.implicitHeight, progressBarLayout.implicitHeight)
IndicatorDelegate {
id: indicator
quickControl: control
indicatorStyle: styleReader.indicator
secondProgress: control.visualPosition
x: indicatorItem.x
y: indicatorItem.y
width: indicatorItem.width
height: indicatorItem.height
Connections {
target: control
function onIndeterminateChanged() {
if (indeterminate) {
control.contentItem.firstProgress = 0
control.contentItem.secondProgress = 0
control.contentItem.indeterminateAnim.restart()
} else {
control.contentItem.indeterminateAnim.stop()
control.contentItem.firstProgress = 0
control.contentItem.secondProgress = Qt.binding(()=>{ return control.visualPosition })
}
}
}
}
Behavior on firstProgress {
enabled: !control.indeterminate && !control.contentItem.indeterminateAnim.running
NumberAnimation {
duration: 100
easing.type: Easing.Linear
Behavior on firstProgress {
enabled: !control.indeterminate && !indicator.indeterminateAnim.running
NumberAnimation {
duration: 100
easing.type: Easing.Linear
}
}
}
Behavior on secondProgress {
enabled: !control.indeterminate && !control.contentItem.indeterminateAnim.running
NumberAnimation {
duration: 100
easing.type: Easing.Linear
Behavior on secondProgress {
enabled: !control.indeterminate && !indicator.indeterminateAnim.running
NumberAnimation {
duration: 100
easing.type: Easing.Linear
}
}
}
property Animation indeterminateAnim: SequentialAnimation {
running: control.indeterminate
loops: Animation.Infinite
NumberAnimation {
target: control.contentItem
property: "secondProgress"
to: 1
duration: 300
easing.type: Easing.Linear
}
NumberAnimation {
target: control.contentItem
property: "firstProgress"
to: 0.95
duration: 600
easing.type: Easing.OutExpo
}
NumberAnimation {
target: control.contentItem
property: "firstProgress"
to: 0
duration: 300
easing.type: Easing.Linear
}
NumberAnimation {
target: control.contentItem
property: "secondProgress"
to: 0.05
duration: 600
easing.type: Easing.OutExpo
property Animation indeterminateAnim: SequentialAnimation {
running: control.indeterminate
loops: Animation.Infinite
NumberAnimation {
target: indicator
property: "secondProgress"
to: 1
duration: 300
easing.type: Easing.Linear
}
NumberAnimation {
target: indicator
property: "firstProgress"
to: 0.95
duration: 600
easing.type: Easing.OutExpo
}
NumberAnimation {
target: indicator
property: "firstProgress"
to: 0
duration: 300
easing.type: Easing.Linear
}
NumberAnimation {
target: indicator
property: "secondProgress"
to: 0.05
duration: 600
easing.type: Easing.OutExpo
}
}
}
}
@@ -1,18 +1,9 @@
// Copyright (C) 2025 The Qt Company Ltd.
// Copyright (C) 2026 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
// Qt-Security score:significant reason:default
import QtQuick
/*!
\qmltype StyleAnimation
\inqmlmodule Qt.labs.StyleKit
\inherits ParallelAnimation
\brief Animates style property changes during state transitions.
\labs
*/
ParallelAnimation {
id: root
property alias easing: colorAnimation.easing
@@ -45,7 +36,6 @@ ParallelAnimation {
readonly property string __geometryProps:
"$.implicitWidth, $.implicitHeight, "
+ "$.margins, $.leftMargin, $.rightMargin, $.topMargin, $.bottomMargin, "
+ "$.leftPadding, $.rightPadding, $.topPadding, $.bottomPadding, "
+ "$.minimumWidth, "
readonly property string __colorProps:
"$.color, $.border.color, $.image.color, $.shadow.color, "
@@ -57,7 +47,6 @@ ParallelAnimation {
"$.shadow.verticalOffset, $.shadow.horizontalOffset, "
+ "$.shadow.scale, $.shadow.blur, "
function __animateIndicators(doAnimate, anim, props) {
if (!doAnimate)
return
@@ -13,10 +13,7 @@ qhp.QtLabsStyleKit.virtualFolder = qtlabsstylekit
qhp.QtLabsStyleKit.indexTitle = Qt Labs StyleKit
qhp.QtLabsStyleKit.indexRoot =
qhp.QtLabsStyleKit.subprojects = manual qmltypes
qhp.QtLabsStyleKit.subprojects.manual.title = Qt Labs StyleKit
qhp.QtLabsStyleKit.subprojects.manual.indexTitle = Qt Labs StyleKit
qhp.QtLabsStyleKit.subprojects.manual.selectors = group:none
qhp.QtLabsStyleKit.subprojects = qmltypes
qhp.QtLabsStyleKit.subprojects.qmltypes.title = QML Types
qhp.QtLabsStyleKit.subprojects.qmltypes.indexTitle = Qt Labs StyleKit QML Types
@@ -175,6 +175,11 @@ ApplicationWindow {
color: "white"
border.width: 0
}
variations: StyleVariation {
toolBar.background.color: "cornflowerblue"
toolButton.background.color: "skyblue"
toolButton.background.border.color: "aliceblue"
}
}
//! [page]
@@ -273,15 +278,9 @@ ApplicationWindow {
scrollBar {
padding: 0
background {
// For scroll bars, we typically don't want a background, only a
// draggable indicator and a groove.
visible: false
// Note: even if the background is hidden, its implicit size is still used to
// determine the overall size of the scroll bar. So we set implicitHeight equal to
// the height of the indicator, to make it appear on the side of, and not on top
// of, the content area in a ScrollView. For a horizontal scroll bar, on the other
// hand, the width is automatically set by the view to match its own width.
// Set the groove size and color
implicitHeight: 10
color: "white"
}
indicator {
radius: 255
@@ -289,6 +288,7 @@ ApplicationWindow {
// view to be the relative size of the view's content that is currently visible.
implicitHeight: 10
color: "#c0c0c0"
// The indicator is so simple that it doesn't need a foreground
foreground.visible: false
}
vertical {
@@ -303,8 +303,8 @@ ApplicationWindow {
//! [scrollIndicator]
scrollIndicator {
// For scroll indicators, we typically don't want a background, only a
// sliding indicator
// A scroll indicator typically has no groove, just a sliding
// indicator. So hide the background, since it represents the groove.
background.visible: false
indicator {
// For horizontal indicators, we only set the height. The width is set by the
@@ -633,6 +633,14 @@ ApplicationWindow {
width: 200
text: "TextField"
}
Page {
header: ToolBar {
ToolButton {
text: "<"
}
}
}
}
}
}
@@ -0,0 +1,76 @@
// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import Qt.labs.StyleKit
ApplicationWindow {
id: app
width: 1024
height: 800
visible: true
StyleKit.style:
//! [custom control style]
// MyStyle.qml
Style {
id: style
readonly property int myControlType: 0
CustomControl {
controlType: style.myControlType
background {
implicitWidth: 120
implicitHeight: 30
radius: 0
}
hovered.background.color: "lightslategray"
pressed.background.color: "skyblue"
}
}
//! [custom control style]
//! [custom control]
// Main.qml
component MyControl : Rectangle {
StyleReader {
id: styleReader
controlType: StyleKit.style.myControlType
hovered: hoverHandler.hovered
pressed: tapHandler.pressed
palette: app.palette
}
HoverHandler { id: hoverHandler }
TapHandler { id: tapHandler }
implicitWidth: styleReader.background.implicitWidth
implicitHeight: styleReader.background.implicitHeight
color: styleReader.background.color
radius: styleReader.background.radius
Text {
font: styleReader.font
anchors.centerIn: parent
text: "ok"
}
}
//! [custom control]
// The rest of the file is not a part of the docs. It just implements a small
// UI to allow testing the style from the command line using the 'qml' app.
ScrollView {
anchors.fill: parent
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
MyControl {
}
}
}
}
@@ -0,0 +1,106 @@
// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import Qt.labs.StyleKit
ApplicationWindow {
id: app
width: 1024
height: 800
visible: true
StyleKit.style: Style {
//! [transition]
comboBox {
background.color: "lightgray"
hovered.background.color: "plum"
indicator.color: "white"
hovered.indicator.color: "pink"
hovered.indicator.border.width: 4
transition: Transition {
StyleAnimation {
animateBackgroundColors: true
animateIndicatorColors: true
animateIndicatorBorder: true
easing.type: Easing.OutQuad
duration: 500
}
}
}
//! [transition]
//! [custom transition]
checkBox {
background.color: "lightgray"
hovered.background.color: "plum"
indicator.color: "white"
hovered.indicator.color: "pink"
hovered.indicator.border.width: 4
transition: Transition {
ColorAnimation {
properties: "background.color, background.border.color, background.image.color, background.shadow.color"
+ ", indicator.color, indicator.border.color, indicator.image.color, indicator.shadow.color"
easing.type: Easing.OutQuad
duration: 500
}
NumberAnimation {
properties: "indicator.border.width"
easing.type: Easing.OutQuad
duration: 500
}
}
}
//! [custom transition]
//! [mixed transition]
slider {
handle.color: "white"
hovered.handle.color: "seagreen"
hovered.handle.border.width: 8
transition: Transition {
StyleAnimation {
animateHandleColors: true
easing.type: Easing.OutQuad
duration: 500
}
NumberAnimation {
properties: "handle.border.width"
easing.type: Easing.OutBounce
duration: 1000
}
}
}
//! [mixed transition]
}
// The rest of the file is not a part of the docs. It just implements a small
// UI to allow testing the style from the command line using the 'qml' app.
ScrollView {
anchors.fill: parent
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
ComboBox {
model: ["apple", "banana", "orange"]
}
CheckBox {
text: "CheckBox"
}
Slider {
width: 200
}
}
}
}
@@ -0,0 +1,60 @@
// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import Qt.labs.StyleKit
ApplicationWindow {
id: app
width: 1024
height: 800
visible: true
StyleKit.style: Style {
button.pressed.background.color: "magenta"
button.hovered.text.bold: true
}
//! [custom control]
// Main.qml
component CustomButton : Rectangle {
StyleReader {
id: styleReader
controlType: StyleReader.Button
hovered: hoverHandler.hovered
pressed: tapHandler.pressed
palette: app.palette
}
implicitWidth: styleReader.background.implicitWidth
implicitHeight: styleReader.background.implicitHeight
color: styleReader.background.color
radius: styleReader.background.radius
HoverHandler { id: hoverHandler }
TapHandler { id: tapHandler }
Text {
font: styleReader.font
anchors.centerIn: parent
text: "ok"
}
}
//! [custom control]
// The rest of the file is not a part of the docs. It just implements a small
// UI to allow testing the style from the command line using the 'qml' app.
ScrollView {
anchors.fill: parent
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
CustomButton {
}
}
}
}
@@ -9,6 +9,7 @@
\ingroup qtquickcontrols-examples
\image qtlabsstylekit-example.webp
{StyleKit demo showing buttons, checkboxes, sliders, and settings}
This example shows how to style applications with \l {Qt Labs StyleKit}{StyleKit}.
It includes several styles that each demonstrate different aspects of styling:
@@ -38,4 +39,7 @@
\sa {Qt Labs StyleKit}, Style, Theme
\include examples-run.qdocinc
\note This example requires Qt Shader Tools, an optional library available
through the Qt Maintenance Tool.
*/
@@ -18,5 +18,5 @@
\snippet ReferenceFallbackStyle.qml 1
\sa {Style::fallbackStyle}{Style.fallbackStyle}, {qtlabsstylekit-property-resolution.html}{Property Resolution}
\sa {Style::fallbackStyle}{Style.fallbackStyle}
*/
@@ -3,7 +3,7 @@
/*!
\qmlmodule Qt.labs.StyleKit
\techpreview
\preliminary
\title Qt Labs StyleKit QML Types
\ingroup qmlmodules
\brief Provides QML types for declarative styling of Qt Quick Controls.
@@ -113,7 +113,7 @@ BaseStyle {
}
comboBox {
text.alignment: Qt.AlignVCenter
text.alignment: Qt.AlignVCenter | Qt.AlignLeft
background.implicitWidth: 150
indicator {
color: __transparent
@@ -204,7 +204,6 @@ BaseStyle {
padding: 0
background {
implicitHeight: 10
visible: false
}
indicator {
implicitHeight: 10
@@ -22,7 +22,7 @@ RectangularShadow {
topRightRadius: delegateStyle.topRightRadius
bottomLeftRadius: delegateStyle.bottomLeftRadius
bottomRightRadius: delegateStyle.bottomRightRadius
visible: delegateStyle.shadow.visible
visible: delegateStyle.visible && delegateStyle.shadow.visible
offset: Qt.vector2d(
delegateStyle.shadow.horizontalOffset,
delegateStyle.shadow.verticalOffset)
@@ -73,8 +73,24 @@ bool QQStyleKitDelegateContainer::usingDefaultDelegate() const
void QQStyleKitDelegateContainer::updateImplicitSize()
{
setImplicitWidth(m_delegateInstance ? m_delegateInstance->implicitWidth() : 0);
setImplicitHeight(m_delegateInstance ? m_delegateInstance->implicitHeight() : 0);
qreal implicitWidth = 0;
qreal implicitHeight = 0;
if (m_delegateInstance) {
/* The delegate instance is expected to bind its implicit size to the
* value from the style, but may override it if needed. */
implicitWidth = m_delegateInstance->implicitWidth();
implicitHeight = m_delegateInstance->implicitHeight();
} else if (m_delegateProperties) {
/* The delegate instance may not exist yet (e.g. because it is hidden),
* but the container should still report the style's implicit size so
* that it reserves the correct space in a layout. */
implicitWidth = qMax(m_delegateProperties->minimumWidth(), m_delegateProperties->implicitWidth());
implicitHeight = m_delegateProperties->implicitHeight();
}
setImplicitWidth(implicitWidth);
setImplicitHeight(implicitHeight);
}
void QQStyleKitDelegateContainer::maybeCreateDelegate()
@@ -244,6 +260,8 @@ void QQStyleKitDelegateContainer::componentComplete()
maybeCreateShadow();
}
});
updateImplicitSize();
}
QT_END_NAMESPACE
@@ -58,15 +58,12 @@ static qreal vAlignY(QQStyleKitLayoutItem *li, qreal containerY, qreal container
const auto itemHeight = height(li, containerHeight);
const auto vAlign = li->alignment() & Qt::AlignVertical_Mask;
const auto margins = li->margins();
switch (vAlign) {
case Qt::AlignTop:
if (vAlign & Qt::AlignTop)
return containerY + margins.top();
case Qt::AlignBottom:
if (vAlign & Qt::AlignBottom)
return containerY + containerHeight - itemHeight - margins.bottom();
default: // AlignVCenter
return containerY + margins.top()
+ (containerHeight - margins.top() - margins.bottom() - itemHeight) / 2.0;
}
return containerY + margins.top()
+ (containerHeight - margins.top() - margins.bottom() - itemHeight) / 2.0;
}
QQStyleKitLayoutItem::QQStyleKitLayoutItem(QObject *parent)
@@ -104,7 +104,7 @@ QQStyleKitAttached::QQStyleKitAttached(QObject *parent)
: QObject(parent)
{
connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, [this](){
if (m_style)
if (m_style && QString::compare(m_style->themeName(), "System"_L1, Qt::CaseInsensitive) == 0)
m_style->recreateTheme();
});
}
@@ -0,0 +1,229 @@
// Copyright (C) 2026 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 <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
/*!
\qmltype StyleAnimation
\inqmlmodule Qt.labs.StyleKit
\inherits ParallelAnimation
\brief A convenience type for animating style properties.
StyleAnimation is an \l [QtQuick]{Animation} that animates style properties
when a control changes \l {ControlStateStyle}{state}. For example, setting
\l animateBackgroundColors to \c true will animate all color properties of
the \l {ControlStyle::}{background}, such as \l {DelegateStyle::color}{background.color},
\l {BorderStyle::color}{background.border.color}, \l {ImageStyle::color}{background.image.color},
and \l {ShadowStyle::color}{background.shadow.color}. It is used inside a
\l {ControlStyle::}{transition}:
\snippet StyleAnimationSnippets.qml transition
StyleAnimation is just a convenience \l [QtQuick]{Animation} that handles listing the
affected style properties automatically. The snippet above could also be written as:
\snippet StyleAnimationSnippets.qml custom transition
A StyleAnimation can also be used together with other animations:
\snippet StyleAnimationSnippets.qml mixed transition
\labs
\sa {ControlStyleProperties::transition}{ControlStyle.transition}, ControlStyle
*/
/*!
\qmlproperty EasingCurve StyleAnimation::easing
The easing curve applied to the animation.
\sa duration, PropertyAnimation
*/
/*!
\qmlproperty int StyleAnimation::duration
The duration in milliseconds of the animation.
\sa easing, PropertyAnimation
*/
/*!
\qmlproperty bool StyleAnimation::animateColors
When \c true, animates all color properties across all delegates. This is a
shorthand for enabling \l animateBackgroundColors, \l animateHandleColors,
and \l animateIndicatorColors at once.
Note that \l ColorAnimation also animates all color properties that
change during a transition when \l {PropertyAnimation::}{property} and
\l {PropertyAnimation::}{properties} are both left unset. This makes it a
an alternative to StyleAnimation when only colors need to be animated.
\sa animateBackgroundColors, animateHandleColors, animateIndicatorColors
*/
/*!
\qmlproperty bool StyleAnimation::animateControlGeometry
When \c true, animates the control's geometry properties, such as
\l {ControlStyleProperties::spacing}{spacing} and
\l {ControlStyleProperties::padding}{padding}.
*/
/*!
\qmlproperty bool StyleAnimation::animateBackgroundColors
When \c true, animates the color properties of the
\l {ControlStyleProperties::}{background} delegate,
including \l {BorderStyle::color}{border} and \l {ShadowStyle::color}{shadow} colors.
\sa animateColors, animateBackgroundGeometry
*/
/*!
\qmlproperty bool StyleAnimation::animateBackgroundGeometry
When \c true, animates the geometry properties of the
\l {ControlStyleProperties::}{background} delegate,
such as \l {DelegateStyle::implicitWidth}{implicit size}
and \l {DelegateStyle::}{margins}.
*/
/*!
\qmlproperty bool StyleAnimation::animateBackgroundRadii
When \c true, animates the corner \l {DelegateStyle::radius}{radii} of the
\l {ControlStyleProperties::}{background} delegate.
*/
/*!
\qmlproperty bool StyleAnimation::animateBackgroundBorder
When \c true, animates the \l {BorderStyle::width}{border width} of the
\l {ControlStyleProperties::}{background} delegate.
*/
/*!
\qmlproperty bool StyleAnimation::animateBackgroundShadow
When \c true, animates the shadow properties of the
\l {ControlStyleProperties::}{background} delegate,
such as \l {ShadowStyle::verticalOffset}{vertical offset},
\l {ShadowStyle::horizontalOffset}{horizontal offset},
\l {ShadowStyle::scale}{scale}, and \l {ShadowStyle::blur}{blur}.
The shadow \l {ShadowStyle::color}{color} is animated by \l animateHandleColors.
*/
/*!
\qmlproperty bool StyleAnimation::animateBackgroundScaleAndRotation
When \c true, animates the and rotation of the
\l {ControlStyleProperties::}{background} delegate.
*/
/*!
\qmlproperty bool StyleAnimation::animateHandleColors
When \c true, animates the color properties of the
\l {ControlStyleProperties::}{handle} delegate and its \l {HandleStyle}{sub-handles},
including \l {BorderStyle::color}{border} and \l {ShadowStyle::color}{shadow} colors.
\sa animateColors, animateHandleGeometry
*/
/*!
\qmlproperty bool StyleAnimation::animateHandleGeometry
When \c true, animates the geometry properties of the
\l {ControlStyleProperties::}{handle} delegate and its \l {HandleStyle}{sub-handles},
such as \l {DelegateStyle::implicitWidth}{implicit size} and
\l {DelegateStyle::}{margins}.
*/
/*!
\qmlproperty bool StyleAnimation::animateHandleRadii
When \c true, animates the corner \l {DelegateStyle::radius}{radii} of the
\l {ControlStyleProperties::}{handle} delegate and its \l {HandleStyle}{sub-handles}.
*/
/*!
\qmlproperty bool StyleAnimation::animateHandleBorder
When \c true, animates the \l {BorderStyle::width}{border width} of the
\l {ControlStyleProperties::}{handle} delegate and its \l {HandleStyle}{sub-handles}.
*/
/*!
\qmlproperty bool StyleAnimation::animateHandleShadow
When \c true, animates the shadow properties of the
\l {ControlStyleProperties::}{handle} delegate and its \l {HandleStyle}{sub-handles},
such as \l {ShadowStyle::verticalOffset}{vertical offset},
\l {ShadowStyle::horizontalOffset}{horizontal offset},
\l {ShadowStyle::scale}{scale}, and \l {ShadowStyle::blur}{blur}.
The shadow \l {ShadowStyle::color}{color} is animated by \l animateHandleColors.
*/
/*!
\qmlproperty bool StyleAnimation::animateHandleScaleAndRotation
When \c true, animates the \l {DelegateStyle::}{scale} and \l {DelegateStyle::}{rotation} of the
\l {ControlStyleProperties::}{handle} delegate and its \l {HandleStyle}{sub-handles}.
*/
/*!
\qmlproperty bool StyleAnimation::animateIndicatorColors
When \c true, animates the color properties of the
\l {ControlStyleProperties::}{indicator} delegate and its \l {IndicatorStyle}{sub-indicators},
including \l {BorderStyle::color}{border} and \l {ShadowStyle::color}{shadow} colors.
\sa animateColors, animateIndicatorGeometry
*/
/*!
\qmlproperty bool StyleAnimation::animateIndicatorGeometry
When \c true, animates the geometry properties of the
\l {ControlStyleProperties::}{indicator} delegate and its \l {IndicatorStyle}{sub-indicators},
such as \l {DelegateStyle::implicitWidth}{implicit size} and \l {DelegateStyle::}{margins}.
*/
/*!
\qmlproperty bool StyleAnimation::animateIndicatorRadii
When \c true, animates the corner \l {DelegateStyle::radius}{radii} of the
\l {ControlStyleProperties::}{indicator} delegate and its \l {IndicatorStyle}{sub-indicators}.
*/
/*!
\qmlproperty bool StyleAnimation::animateIndicatorBorder
When \c true, animates the \l {BorderStyle::width}{border width} of the
\l {ControlStyleProperties::}{indicator} delegate and its \l {IndicatorStyle}{sub-indicators}.
*/
/*!
\qmlproperty bool StyleAnimation::animateIndicatorShadow
When \c true, animates the shadow properties of the
\l {ControlStyleProperties::}{indicator} delegate and its \l {IndicatorStyle}{sub-indicators},
such as \l {ShadowStyle::verticalOffset}{vertical offset},
\l {ShadowStyle::horizontalOffset}{horizontal offset},
\l {ShadowStyle::scale}{scale}, and \l {ShadowStyle::blur}{blur}.
The shadow \l {ShadowStyle::color}{color} is animated by \l animateHandleColors.
*/
/*!
\qmlproperty bool StyleAnimation::animateIndicatorScaleAndRotation
When \c true, animates the scale and rotation of the
\l {ControlStyleProperties::}{indicator} delegate and its \l {IndicatorStyle}{sub-indicators}.
*/
QT_END_NAMESPACE
@@ -9,131 +9,28 @@
QT_BEGIN_NAMESPACE
// ************* ControlStateStyle ****************
// ************* ControlStyleProperties ****************
/*!
\qmltype ControlStateStyle
\qmltype ControlStyleProperties
\inqmlmodule Qt.labs.StyleKit
\brief Defines the style for a control in a given state.
\brief Defines the stylable properties for a control.
A ControlStateStyle contains the properties that can be styled for each
state a control can be in. This includes the visual building blocks
\l background, \l indicator, \l handle, and \l text, along with
layout properties such as \l padding and \l spacing, and more.
ControlStyleProperties defines the stylable properties for a
control: the visual building blocks \l background, \l indicator, \l handle,
and \l text, as well as layout properties like \l padding, \l spacing,
and \l transition.
A \l ControlStyle inherits ControlStateStyle, since it represents the
\e normal state — that is, properties set directly on a
ControlStyle describe how the control looks when no other state
is active. State-specific overrides are set through nested states
such as \l pressed, \l hovered, and \l checked, and can be
animated by a \l transition.
Nested states are not mutually exclusive. Multiple states can be active at
the same time — for example, a button can be both hovered and
pressed simultaneously. When several states are active, all matching
state overrides are applied. If the same property is set in multiple
active states, conflicts are resolved using the following priority
order: \l pressed, \l hovered, \l highlighted, \l focused,
\l checked, \l vertical. So for example \c {pressed.background.color}
wins over \c {checked.background.color} if the control is both
\l pressed and \l checked.
The \l disabled state is an exception: a disabled control cannot
be interacted with, so the \l pressed, \l hovered, \l highlighted,
and \l focused states will not apply. However, \l disabled can
still be combined with states like \l checked and \l vertical.
The more deeply nested a state is, the more qualified it is.
For example, \c {hovered.pressed.background.color} takes precedence over
\c {hovered.background.color} when both \l hovered and \l pressed are
active. The nesting order does not matter:
\c {hovered.pressed} and \c {pressed.hovered} are equivalent.
However, if both forms are used at the same time, which one wins is
undefined.
Deeper nesting of states can also be used to resolve conflicts. If the same property
is set in both \l hovered and \l checked, the priority order means
the \l hovered value wins. If you would rather have the checked value win, or
use an altogether different value in that situation, you can override
the property in \c {hovered.checked}, which then takes precedence over both.
The following snippet shows how to style a button differently
depending on its state:
\snippet ControlStateStyle_states.qml States
The style properties are accessed through inherited types such as
\l ControlStyle and \l StyleReader.
\labs
\sa ControlStyle, DelegateStyle,
{qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
\sa ControlStyle, StyleReader, ControlStateStyle, DelegateStyle
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::pressed
Style overrides applied when the control is pressed (e.g. the
user is holding down a mouse button on it).
\sa hovered, highlighted, focused, checked, vertical, disabled
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::hovered
Style overrides applied when the mouse cursor is over the control.
\sa pressed, highlighted, focused, checked, vertical, disabled
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::highlighted
Style overrides applied when the control is highlighted. A control
is typically highlighted in order to draw the user's attention towards it.
\sa pressed, hovered, focused, checked, vertical, disabled
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::focused
Style overrides applied when the control has input focus.
\sa pressed, hovered, highlighted, checked, vertical, disabled
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::checked
Style overrides applied when the control is in a checked or
toggled-on state (e.g. a checked \l CheckBox or a toggled
\l Button).
\sa pressed, hovered, highlighted, focused, vertical, disabled
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::vertical
Style overrides applied when the control has a vertical
orientation (e.g. a vertical \l Slider or \l ScrollBar).
\sa pressed, hovered, highlighted, focused, checked, disabled
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::disabled
Style overrides applied when the control is disabled.
A disabled control cannot be interacted with, so \l pressed,
\l hovered, \l highlighted, and \l focused will not be applied
at the same time as disabled.
\sa pressed, hovered, highlighted, focused, checked, vertical
*/
/*!
\qmlproperty DelegateStyle ControlStateStyle::background
\qmlproperty DelegateStyle ControlStyleProperties::background
Grouped property for styling the background of a control.
@@ -153,7 +50,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
\qmlproperty real ControlStateStyle::bottomPadding
\qmlproperty real ControlStyleProperties::bottomPadding
The \l [QtQuickControls]{Control Layout}{bottom padding}
of the control. If not set, falls back to \l padding.
@@ -162,7 +59,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
\qmlproperty HandleStyle ControlStateStyle::handle
\qmlproperty HandleStyle ControlStyleProperties::handle
Grouped property for styling the handle of a control.
@@ -175,7 +72,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
\qmlproperty IndicatorStyle ControlStateStyle::indicator
\qmlproperty IndicatorStyle ControlStyleProperties::indicator
Grouped property for styling the indicator of a control. For a
\l {AbstractStylableControls::}{checkBox}, the indicator is the
@@ -187,7 +84,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
\qmlproperty real ControlStateStyle::leftPadding
\qmlproperty real ControlStyleProperties::leftPadding
The \l [QtQuickControls]{Control Layout}{left padding} of the control. If not set, falls back to
\l padding.
@@ -196,7 +93,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
\qmlproperty real ControlStateStyle::padding
\qmlproperty real ControlStyleProperties::padding
The uniform spacing between the
\l [QtQuickControls]{Control Layout}{control's content area and the
@@ -208,7 +105,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
\qmlproperty real ControlStateStyle::rightPadding
\qmlproperty real ControlStyleProperties::rightPadding
The \l [QtQuickControls]{Control Layout}{right padding} of the control.
If not set, falls back to \l padding.
@@ -217,20 +114,20 @@ QT_BEGIN_NAMESPACE
*/
/*!
\qmlproperty real ControlStateStyle::spacing
\qmlproperty real ControlStyleProperties::spacing
The spacing between visual elements inside the control, for example
between an indicator and a label.
*/
/*!
\qmlproperty TextStyle ControlStateStyle::text
\qmlproperty TextStyle ControlStyleProperties::text
Grouped property for styling the text label of a control.
*/
/*!
\qmlproperty real ControlStateStyle::topPadding
\qmlproperty real ControlStyleProperties::topPadding
The \l [QtQuickControls]{Control Layout}{top padding}
of the control. If not set, falls back to \l padding.
@@ -239,10 +136,11 @@ QT_BEGIN_NAMESPACE
*/
/*!
\qmlproperty Transition ControlStateStyle::transition
\qmlproperty Transition ControlStyleProperties::transition
A \l [QtQuick]{Transition} used to animate style properties when the
control enters a new state, such as \l hovered or \l pressed. If set
control enters a new state, such as \l {ControlStateStyle::}{hovered}
or \l {ControlStateStyle::}{pressed}. If set
to \c null (the default), property changes are applied immediately
without animation.
@@ -269,8 +167,8 @@ QT_BEGIN_NAMESPACE
\brief Defines the visual appearance of an element within a control.
DelegateStyle describes the visual appearance of a visual element
within a \l ControlStyle, such as its \l {ControlStateStyle::}{background},
\l {ControlStateStyle::}{indicator}, or a sub-element like
within a \l ControlStyle, such as its \l {ControlStyleProperties::}{background},
\l {ControlStyleProperties::}{indicator}, or a sub-element like
\l {IndicatorStyle::foreground}{indicator.foreground}.
It provides properties for controlling
\l {DelegateStyle::implicitWidth}{size},
@@ -376,7 +274,8 @@ QT_BEGIN_NAMESPACE
\l {Qt Quick Controls}{Qt Quick Control}.
The default value is \c null, in which case \l StyledItem is used
for rendering instead.
for rendering instead. If you want to hide the delegate altogether
(and prevent if from being created), set \l visible to \c false.
The delegate needs to define the following required properties:
\qml
@@ -400,7 +299,7 @@ QT_BEGIN_NAMESPACE
\note If a DelegateStyle has a \l {shadow}{drop shadow} defined, it will be
drawn separately by a \l {ShadowStyle::delegate}{shadow delegate.}
\sa data, StyledItem
\sa data, visible, StyledItem
*/
/*!
@@ -550,6 +449,9 @@ QT_BEGIN_NAMESPACE
should not draw a background, such as \l [QtQuickControls]{CheckBox},
\l [QtQuickControls]{RadioButton}, and \l [QtQuickControls]{Slider}.
When \c visible is \c false, the \l delegate will not be created. If you need
a hidden delegate to be created, consider setting \l opacity to \c 0 instead.
\sa opacity
*/
@@ -866,7 +768,7 @@ QT_BEGIN_NAMESPACE
\l {TextStyle::}{italic}, and
\l {TextStyle::pointSize}{font size}.
It is available as the \l {ControlStateStyle::text}{text}
It is available as the \l {ControlStyleProperties::text}{text}
grouped property on every \l ControlStateStyle.
\labs
@@ -148,7 +148,7 @@ signals:
// ************* QQStyleKitBorderProperties ****************
class QQStyleKitBorderProperties : public QQStyleKitPropertyGroup
class Q_LABSSTYLEKIT_EXPORT QQStyleKitBorderProperties : public QQStyleKitPropertyGroup
{
Q_OBJECT
Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged FINAL)
@@ -546,7 +546,7 @@ signals:
* only be read), and if so, offer a storage for storing those values. That storage is typically
* a map that stores _only_ the properties that are written to, and nothing else.
*/
class QQStyleKitControlProperties : public QQStyleKitPropertyGroup
class Q_LABSSTYLEKIT_EXPORT QQStyleKitControlProperties : public QQStyleKitPropertyGroup
{
Q_OBJECT
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged FINAL)
@@ -560,8 +560,9 @@ class QQStyleKitControlProperties : public QQStyleKitPropertyGroup
Q_PROPERTY(QQStyleKitIndicatorWithSubTypes *indicator READ indicator NOTIFY indicatorChanged FINAL)
Q_PROPERTY(QQStyleKitTextProperties *text READ text NOTIFY textChanged FINAL)
Q_PROPERTY(QQuickTransition *transition READ transition WRITE setTransition NOTIFY transitionChanged FINAL)
// This type is merged with ControlState, from a QML API point of view
QML_ANONYMOUS
QML_UNCREATABLE("This component can only be instantiated by StyleKit")
QML_NAMED_ELEMENT(ControlStyleProperties)
public:
QQStyleKitControlProperties(QQSK::PropertyGroup group, QObject *parent = nullptr);
@@ -3,7 +3,6 @@
#include "qqstylekitcontrols_p.h"
#include "qqstylekitcontrol_p.h"
#include "qqstylekitvariation_p.h"
#include "qqstylekitcustomcontrol_p.h"
QT_BEGIN_NAMESPACE
@@ -63,7 +62,7 @@ QT_BEGIN_NAMESPACE
\snippet ControlsSnippets.qml abstractButton
\sa {qtlabsstylekit-controls.html}{Control Types}
\sa {Qt Labs StyleKit}{Control Types}
*/
/*!
@@ -174,6 +173,13 @@ QT_BEGIN_NAMESPACE
Unset properties fall back to \l pane.
Note that the \l [QtQuickControls]{Page::header}{header} and
\l [QtQuickControls]{Page::footer}{footer} of a \l [QtQuickControls]{Page} are
typically set by the application to a \l [QtQuickControls]{ToolBar} or
\l [QtQuickControls]{TabBar}, and those controls are styled separately.
To give a \l [QtQuickControls]{ToolBar} alternative styling when used inside
a \l [QtQuickControls]{Page}, use a \l StyleVariation:
\snippet ControlsSnippets.qml page
*/
@@ -229,8 +235,8 @@ QT_BEGIN_NAMESPACE
\qmlproperty ControlStyle AbstractStylableControls::scrollBar
Grouped property for styling \l [QtQuickControls]{ScrollBar}.
For a scroll bar, the groove is styled through the indicator, while the handle is
styled through the indicator's foreground.
The groove is styled through \l {ControlStyleProperties::}{background}, and the
handle through \l {ControlStyleProperties::}{indicator}.
Unset properties fall back to \l control.
@@ -241,6 +247,8 @@ QT_BEGIN_NAMESPACE
\qmlproperty ControlStyle AbstractStylableControls::scrollIndicator
Grouped property for styling \l [QtQuickControls]{ScrollIndicator}.
The groove is styled through \l {ControlStyleProperties::}{background}, and the
handle through \l {ControlStyleProperties::}{indicator}.
Unset properties fall back to \l control.
@@ -407,17 +415,6 @@ QQStyleKitControl* QQStyleKitControls::getControl(QQStyleKitExtendableControlTyp
return m_controls[controlType];
}
QList<QQStyleKitVariation *> QQStyleKitControls::variations() const
{
QList<QQStyleKitVariation *> list;
for (auto *obj : children()) {
if (auto *variation = qobject_cast<QQStyleKitVariation *>(obj))
list.append(variation);
}
return list;
}
#define IMPLEMENT_ACCESSORS(NAME, TYPE) \
QQStyleKitControl *QQStyleKitControls::NAME() const \
{ \
@@ -109,8 +109,6 @@ public:
Q_INVOKABLE QQStyleKitControl *getControl(QQStyleKitExtendableControlType controlType) const;
QList<QQStyleKitVariation *> variations() const;
QQmlListProperty<QObject> data();
const QList<QObject *> children() const;
@@ -6,6 +6,129 @@
QT_BEGIN_NAMESPACE
// ************* ControlStateStyle ****************
/*!
\qmltype ControlStateStyle
\inqmlmodule Qt.labs.StyleKit
\inherits ControlStyleProperties
\brief Describes the style of a control in a given state.
ControlStateStyle describes the style of a control in a particular state.
A \l ControlStyle inherits ControlStateStyle, since it represents the
\e normal state — properties set directly on a \l ControlStyle describe
how the control looks when no other state is active. State-specific
overrides are then set through nested states such as \l pressed,
\l hovered, and \l checked.
Nested states are not mutually exclusive. Multiple states can be active at
the same time — for example, a button can be both hovered and
pressed simultaneously. When several states are active, all matching
state overrides are applied. If the same property is set in multiple
active states, conflicts are resolved using the following priority
order: \l pressed, \l hovered, \l highlighted, \l focused,
\l checked, \l vertical. So for example \c {pressed.background.color}
wins over \c {checked.background.color} if the control is both
\l pressed and \l checked.
The \l disabled state is an exception: a disabled control cannot
be interacted with, so the \l pressed, \l hovered, \l highlighted,
and \l focused states will not apply. However, \l disabled can
still be combined with states like \l checked and \l vertical.
The more deeply nested a state is, the more qualified it is.
For example, \c {hovered.pressed.background.color} takes precedence over
\c {hovered.background.color} when both \l hovered and \l pressed are
active. The nesting order does not matter:
\c {hovered.pressed} and \c {pressed.hovered} are equivalent.
However, if both forms are used at the same time, which one wins is
undefined.
Deeper nesting of states can also be used to resolve conflicts. If the same property
is set in both \l hovered and \l checked, the priority order means
the \l hovered value wins. If you would rather have the checked value win, or
use an altogether different value in that situation, you can override
the property in \c {hovered.checked}, which then takes precedence over both.
The following snippet shows how to style a button differently
depending on its state:
\snippet ControlStateStyle_states.qml States
\labs
\sa ControlStyle, DelegateStyle,
{qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::pressed
Style overrides applied when the control is \l {StyleReader::}{pressed}.
\sa hovered, highlighted, focused, checked, vertical, disabled,
{StyleReader::pressed}{StyleReader.pressed}
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::hovered
Style overrides applied when the control is \l {StyleReader::}{hovered}.
\sa pressed, highlighted, focused, checked, vertical, disabled,
{StyleReader::hovered}{StyleReader.hovered}
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::highlighted
Style overrides applied when the control is \l {StyleReader::}{highlighted}.
\sa pressed, hovered, focused, checked, vertical, disabled,
{StyleReader::highlighted}{StyleReader.highlighted}
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::focused
Style overrides applied when the control is \l {StyleReader::}{focused}.
\sa pressed, hovered, highlighted, checked, vertical, disabled,
{StyleReader::focused}{StyleReader.focused}
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::checked
Style overrides applied when the control is \l {StyleReader::}{checked}.
\sa pressed, hovered, highlighted, focused, vertical, disabled,
{StyleReader::checked}{StyleReader.checked}
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::vertical
Style overrides applied when the control is \l {StyleReader::}{vertical}
(for example, a vertical \l Slider or \l ScrollBar).
\sa pressed, hovered, highlighted, focused, checked, disabled,
{StyleReader::vertical}{StyleReader.vertical}
*/
/*!
\qmlproperty ControlStateStyle ControlStateStyle::disabled
Style overrides applied when the control is \l {StyleReader::}{disabled}.
A disabled control cannot be interacted with, so \l pressed,
\l hovered, \l highlighted, and \l focused will not be applied
at the same time as disabled.
\sa pressed, hovered, highlighted, focused, checked, vertical,
{StyleReader::enabled}{StyleReader.enabled}
*/
QQStyleKitControlState::QQStyleKitControlState(QObject *parent)
: QQStyleKitControlProperties(QQSK::PropertyGroup::Control, parent)
{
@@ -9,13 +9,52 @@ QT_BEGIN_NAMESPACE
\qmltype CustomControl
\inqmlmodule Qt.labs.StyleKit
\inherits ControlStyle
\brief Defines styling for a custom control type.
\brief Defines styling for a custom (non-built-in) control.
CustomControl allows you to define and style controls that are not part of
\l {Qt Quick Controls}. This is convenient if you have additional controls
or components that should be styled according to the active \l Style and \l Theme.
Like the built-in control types (such as
\l {AbstractStylableControls::}{abstractButton},
\l {AbstractStylableControls::}{pane}, and
\l {AbstractStylableControls::}{slider}), CustomControl inherits
\l ControlStyle. Unlike built-in types, which are implicitly connected to
their control type, a CustomControl requires \l controlType to be set
explicitly. Apart from that, they work exactly the same way.
A \l Style or \l Theme can define as many custom controls as needed, and a
CustomControl in a Theme can have the same \l controlType as one in the \l Style.
That is no different from, for example, a \l {AbstractStylableControls::}{slider}
being styled by both the \l Style and the \l Theme. The fallback logic is the same.
Any style properties not set on a CustomControl fall back to those set
on a \l {AbstractStylableControls::}{control}.
The following snippet shows how to define styling for a custom control:
\snippet CustomControlSnippets.qml custom control style
And the following snippet shows an example on how to implement a custom
control that uses it:
\snippet CustomControlSnippets.qml custom control
\labs
\sa StyleReader, ControlStyle, Style
*/
/*!
\qmlproperty int CustomControl::controlType
A unique integer identifying this custom control type. Set the same value
on the \l {StyleReader::controlType}{StyleReader.controlType} in your
custom control implementation to connect it to this style definition.
Custom control types must be in the range \c 0 to \c 100000.
\sa {StyleReader::controlType}{StyleReader.controlType}
*/
using namespace Qt::StringLiterals;
@@ -6,6 +6,7 @@
#include "qqstylekitcontrols_p.h"
#include "qqstylekitstyle_p.h"
#include "qqstylekittheme_p.h"
#include "qqstylekitvariation_p.h"
QT_BEGIN_NAMESPACE
@@ -27,17 +28,18 @@ QT_BEGIN_NAMESPACE
To start tracing, assign the \l{The id Attribute}{id} of a \l Control to the \l control property:
\code
StyleKit.debug.control = myButton
StyleKit.debug.control: myButton
\endcode
Each resolved property is printed as a single line showing where the value
came from and what it resolved to:
\code
[read] StyleReader[Normal].button.background.color -> button[Normal] = #ff0000
[read] StyleReader[Hovered].button.background.color -> MyStyle.Theme(Dark).button[Hovered] = #ff0000
\endcode
Use \l filter to limit the output to properties of interest.
Tracing the property reads of a StyleReader can produce a large amount of output. Use \l filter to
limit the output to properties of interest.
\labs
@@ -47,7 +49,7 @@ QT_BEGIN_NAMESPACE
/*!
\qmlproperty Item StyleKitDebug::control
The \l{The id Attribute}{id} of the \l Control to trace.
The \l{The id Attribute}{id} of the \l Control or \l StyleReader to trace.
When set, StyleKit logs all style property reads
for this item to the debug output. Set to \c null to stop tracing.
@@ -61,10 +63,21 @@ QT_BEGIN_NAMESPACE
A regular expression used to filter the debug output. Only lines matching
the pattern are printed. By default, all output is shown.
For example, to show only reads of the background color:
For example, to only trace the background color:
\code
StyleKit.debug.filter = "background.color"
StyleKit.debug.filter: "background.color"
\endcode
To trace all background colors, including properties
such as \l {DelegateStyle::color}{background.color},
\l {BorderStyle::color}{background.border.color},
\l {ImageStyle::color}{background.image.color},
and \l {ShadowStyle::color}{background.shadow.color},
use a regular expression:
\code
StyleKit.debug.filter: "background.*color"
\endcode
\sa control
@@ -85,6 +98,9 @@ QString QQStyleKitDebug::enumToString(EnumType enumValue)
}
QString QQStyleKitDebug::objectName(const QObject *obj) {
if (!obj->objectName().isEmpty())
return obj->objectName();
QString str = QString::fromLatin1(obj->metaObject()->className());
int idx = str.indexOf("_QMLTYPE"_L1);
if (idx != -1)
@@ -94,9 +110,7 @@ QString QQStyleKitDebug::objectName(const QObject *obj) {
if (str.startsWith(prefix))
str = str.mid(prefix.length());
}
const QString name = obj->objectName();
if (!name.isEmpty())
str = str + "("_L1 + name + ")"_L1;
return str;
}
@@ -123,16 +137,19 @@ QString QQStyleKitDebug::propertyPath(const QQStyleKitPropertyGroup *group, cons
QString QQStyleKitDebug::controlToString(const QQStyleKitControlProperties *control)
{
if (!control->objectName().isEmpty())
return control->objectName();
const QObject *parentObj = control->parent();
if (!parentObj)
return "<no parent>"_L1;
auto *controls = qobject_cast<const QQStyleKitControls *>(parentObj);
if (!controls) {
return "<"_L1 + QString::fromUtf8(parentObj->metaObject()->className()) + ">"_L1;
}
const int startIndex = QQStyleKitControlProperties::staticMetaObject.propertyOffset();
const int endIndex = QQStyleKitControlProperties::staticMetaObject.propertyCount();
auto *controls = qobject_cast<const QQStyleKitControls *>(parentObj);
if (!controls)
return "<"_L1 + QString::fromUtf8(parentObj->metaObject()->className()) + ">"_L1;
const int startIndex = QQStyleKitControls::staticMetaObject.propertyOffset();
const int endIndex = QQStyleKitControls::staticMetaObject.propertyCount();
const QMetaObject* parentMeta = parentObj->metaObject();
for (int i = startIndex; i < endIndex; ++i) {
@@ -145,7 +162,8 @@ QString QQStyleKitDebug::controlToString(const QQStyleKitControlProperties *cont
if (propObj == control)
return QString::fromUtf8(prop.name());
}
return "<unknown control: no property found>"_L1;
return "<unknown control>"_L1;
}
QString QQStyleKitDebug::objectPath(const QQStyleKitControlProperties *properties, QObject *from)
@@ -161,10 +179,9 @@ QString QQStyleKitDebug::objectPath(const QQStyleKitControlProperties *propertie
path.prepend(theme->name() + kDot);
} else if (auto *theme = qobject_cast<const QQStyleKitTheme *>(obj)) {
// Note: only one theme is instantiated at a time
if (auto style = theme->style())
path.prepend(style->themeName());
else
path.prepend(objectName(obj));
path.prepend("Theme("_L1 + theme->style()->m_effectiveThemeName + ")"_L1);
} else if (auto *variation = qobject_cast<const QQStyleKitVariation *>(obj)) {
path.prepend("StyleVariation("_L1 + variation->name() + ")"_L1);
} else if (auto *control = qobject_cast<const QQStyleKitControl *>(obj)) {
path.prepend(controlToString(control));
} else if (auto *reader = qobject_cast<const QQStyleKitReader *>(obj)) {
@@ -210,7 +227,7 @@ void QQStyleKitDebug::notifyPropertyRead(
QString storagePath;
if (storage->subclass() == QQSK::Subclass::QQStyleKitReader) {
/* We read an interpolated value stored directly in the reader itself. While this
* can be interesting to print out whe debugging the styling engine itself, it
* can be interesting to print out when debugging the styling engine itself, it
* comes across as noise when inspecting control-to-style mappings. Ignore. */
#if 0
storagePath = "[local storage] "_L1;
@@ -111,7 +111,7 @@ void QQStyleKitDelegate::updateImplicitSize()
* 3. Zero
* The implicit size is read-only because it's calculated in C++ from internal
* child items that are intentionally not exposed to QML. */
const qreal impWidthInStyle = m_delegateProperties->implicitWidth();
const qreal impWidthInStyle = qMax(m_delegateProperties->minimumWidth(), m_delegateProperties->implicitWidth());
const qreal impHeightInStyle = m_delegateProperties->implicitHeight();
setImplicitWidth(impWidthInStyle > 0 || !m_imageOverlay ? impWidthInStyle : m_imageOverlay->implicitWidth());
setImplicitHeight(impHeightInStyle > 0 || !m_imageOverlay ? impHeightInStyle : m_imageOverlay->implicitHeight());
@@ -113,11 +113,34 @@ void QQStyleKitPropertyResolver::cacheReaderState(QQSK::State state)
s_cachedStateList.append(QQSK::StateFlag::Disabled);
}
void QQStyleKitPropertyResolver::addVariationToReader(
QQStyleKitReader *styleReader,
QQStyleKitStyleAndThemeBase *styleOrTheme,
QQStyleKitVariation *variation)
{
/* Add the variation to the StyleReader's list of effective variations.
* A variation may appear more than once in a type variation list or in
* an instance variation list, but it only needs to be added once - the
* first in the list will shadow the other ones anyway. */
if (!styleReader->m_effectiveVariations.contains(variation))
styleReader->m_effectiveVariations.append(variation);
/* We also record in which Style or Theme the variation was found, so that
* it only takes effect for that specific Style or Theme during property
* propagation. Note that the same type variation can be used from both the
* Style and the Theme (its ID can be added to several variation lists). */
if (!variation->m_usageContext.contains(styleOrTheme))
variation->m_usageContext.append(styleOrTheme);
}
void QQStyleKitPropertyResolver::addTypeVariationsToReader(
QQStyleKitReader *styleReader,
const QQStyleKitExtendableControlType parentType,
const QQStyleKitStyle *style)
QQStyleKitStyleAndThemeBase *styleOrTheme,
const AttachedVariationList &attachedVariations)
{
const QQStyleKitExtendableControlType styleReaderType = styleReader->controlType();
const auto styleReaderBaseType = baseTypesForType(styleReaderType);
static PropertyPathIds ids;
if (ids.property.property() == QQSK::Property::NoProperty) {
/* ids is made static, since the 'variations' path will be the same for all
@@ -129,40 +152,36 @@ void QQStyleKitPropertyResolver::addTypeVariationsToReader(
ids.subTypeAlternative = PropertyPathId();
}
const auto parentBaseTypes = baseTypesForType(parentType);
const QVariant inStyleVariationsVar = readPropertyInStyle(ids, parentType, parentBaseTypes, style);
if (!inStyleVariationsVar.isValid())
return;
for (const QQStyleKitVariationAttached *attached : attachedVariations) {
const auto parentType = attached->controlType();
const auto parentBaseTypes = baseTypesForType(parentType);
/* Inside each Type Variation, check if the control type that styleReader represents has
* been defined. If so, it means that the variation might affect it, and should therefore be
* added to the style readers list of effective variations. */
const QQStyleKitExtendableControlType styleReaderType = styleReader->controlType();
const auto styleReaderBaseType = baseTypesForType(styleReaderType);
const auto inStyleVariations = *qvariant_cast<QList<QQStyleKitVariation *> *>(inStyleVariationsVar);
for (auto *variation : inStyleVariations) {
if (!variation) {
// This happens if the user adds non QQStyleKitVariation elements to the QML array
/* Search for the 'variations' property set on the parentType, or any of its base
* types, using normal propagation and fallback logic. Unlike when resolving other style
* properties, we limit the search to the style or theme we're processing, since it matters which
* style or theme a variation belongs when they're used to resolve other style properties later. */
const QVariant typeVariationsVariant = readPropertyInRelevantControls(styleOrTheme, ids, parentType, parentBaseTypes);
if (!typeVariationsVariant.isValid())
continue;
}
/* Note: when we read the variations property from the style, it returns the varations
* set in the most specific storage/state/control (because of propagation). And those
* are the only ones that will take effect. This means that even if there are variations
* in the fallback style for the requested type (control) that overrides some properties,
* and the style/theme has variations that overrides something else, the variations in
* the fallback style will, in that case, not be used. The properties not set in the
* effective variation will instead propagate back to be read from the type in the theme
* or the style. This approach is easier to understand and work with, since the propagation
* always flow in one direction, and doesn't jump back and forth between variations, styles
* and themes. */
if (variation->getControl(styleReaderType)) {
styleReader->m_effectiveInStyleVariations.append(variation);
} else {
for (int type : styleReaderBaseType) {
if (variation->getControl(type)) {
styleReader->m_effectiveInStyleVariations.append(variation);
break;
const auto typeVariations = *qvariant_cast<QList<QQStyleKitVariation *> *>(typeVariationsVariant);
for (QQStyleKitVariation *variation : typeVariations) {
/* Inside each type variation, check if the control type that styleReader represents has
* been defined. If so, it means that the variation _might_ affect it, and should therefore
* be added to the style readers list of effective variations. */
if (!variation) {
/* The variation will be nullptr if non-QQStyleKitVariation elements
* are added to the 'variations' list from QML (such as strings). */
continue;
}
if (variation->getControl(styleReaderType)) {
addVariationToReader(styleReader, styleOrTheme, variation);
} else {
for (int type : styleReaderBaseType) {
if (variation->getControl(type))
addVariationToReader(styleReader, styleOrTheme, variation);
}
}
}
@@ -170,8 +189,9 @@ void QQStyleKitPropertyResolver::addTypeVariationsToReader(
}
void QQStyleKitPropertyResolver::addInstanceVariationsToReader(
QQStyleKitReader *styleReader, const QStringList &inAppVariationNames,
const QVarLengthArray<const QQStyleKitControls *, 6> &stylesAndThemes)
QQStyleKitReader *styleReader,
QQStyleKitStyleAndThemeBase *styleOrTheme,
const AttachedVariationList &attachedVariations)
{
/* Add the variations set from the application to the list of effective variations
* in the styleReader. But, to speed up property look-up later on, we only add the
@@ -181,24 +201,22 @@ void QQStyleKitPropertyResolver::addInstanceVariationsToReader(
const QQStyleKitExtendableControlType styleReaderType = styleReader->controlType();
const auto styleReaderBaseTypes = baseTypesForType(styleReaderType);
for (const QString &attachedVariationName : inAppVariationNames) {
for (const QQStyleKitControls *styleOrTheme : stylesAndThemes) {
const QList<QQStyleKitVariation *> variationsInStyleOrTheme = styleOrTheme->variations();
for (QQStyleKitVariation *variationInStyleOrTheme : variationsInStyleOrTheme) {
if (variationInStyleOrTheme->name() != attachedVariationName)
for (const QQStyleKitVariationAttached *attached : attachedVariations) {
for (const QString &instanceVariationName : attached->variations()) {
for (QQStyleKitVariation *variation : styleOrTheme->m_styleVariations) {
if (variation->name() != instanceVariationName)
continue;
/* Invariant: we found a variation in a Style or a Theme with a name that matches
/* Invariant: we found a variation in the given Style or a Theme with a name that matches
* a name in the attached variation list. Check if the found variation contains the
* type, or the subtypes, of the style reader. If not, it doesn't affect it and can
* therefore be skipped. */
if (variationInStyleOrTheme->getControl(styleReaderType)) {
styleReader->m_effectiveInAppVariations.append(variationInStyleOrTheme);
if (variation->getControl(styleReaderType)) {
addVariationToReader(styleReader, styleOrTheme, variation);
} else {
for (int baseType : styleReaderBaseTypes) {
if (variationInStyleOrTheme->getControl(baseType)) {
styleReader->m_effectiveInAppVariations.append(variationInStyleOrTheme);
break;
}
if (variation->getControl(baseType))
addVariationToReader(styleReader, styleOrTheme, variation);
}
}
}
@@ -207,7 +225,7 @@ void QQStyleKitPropertyResolver::addInstanceVariationsToReader(
}
void QQStyleKitPropertyResolver::rebuildVariationsForReader(
QQStyleKitReader *styleReader, const QQStyleKitStyle *style)
QQStyleKitReader *styleReader, QQStyleKitStyle *style)
{
/* Traverse up the parent chain of \a styleReader, and for each parent, look for an
* instance of QQStyleKitVariationAttached. And for each attached object, check if it
@@ -218,42 +236,32 @@ void QQStyleKitPropertyResolver::rebuildVariationsForReader(
* latter should only affect descendant StyleKitReaders of a specific type. */
Q_ASSERT(styleReader->m_effectiveVariationsDirty);
styleReader->m_effectiveVariationsDirty = false;
styleReader->m_effectiveInAppVariations.clear();
styleReader->m_effectiveInStyleVariations.clear();
styleReader->m_effectiveVariations.clear();
const bool hasInstanceVariations = QQStyleKitVariationAttached::s_instanceVariationCount > 0;
const bool hasTypeVariations = QQStyleKitVariation::s_typeVariationCount > 0;
if (!hasTypeVariations && !hasInstanceVariations) {
/* No variations are defined in the current style or theme. In that case
* it doesn't matter if the application has variations set - they
* will anyway not map to any variations in the style. */
if (!style->m_hasVariations)
return;
}
/* We need to search through all variations defined in either the style or the theme,
* including the ones in the fallback styles, since the variation property propagates,
* like all the other style properties. */
QVarLengthArray<const QQStyleKitControls *, 6> stylesAndThemes;
const QQStyleKitStyle *styleOrFallbackStyle = style;
while (styleOrFallbackStyle) {
if (const auto *theme = styleOrFallbackStyle->theme())
stylesAndThemes.append(theme);
stylesAndThemes.append(styleOrFallbackStyle);
styleOrFallbackStyle = styleOrFallbackStyle->fallbackStyle();
}
QObject *parentObj = styleReader;
while (parentObj) {
QObject *attachedObject = qmlAttachedPropertiesObject<QQStyleKitVariation>(parentObj, false);
if (attachedObject) {
auto *variationAtt = static_cast<QQStyleKitVariationAttached *>(attachedObject);
if (hasInstanceVariations)
addInstanceVariationsToReader(styleReader, variationAtt->variations(), stylesAndThemes);
if (hasTypeVariations)
addTypeVariationsToReader(styleReader, variationAtt->controlType(), style);
/* Walk up the parent chain and collect all attached StyleVariation objects that
* may affect this StyleReader. Their variation lists affect instance variations,
* and their control type may affect type variations. */
AttachedVariationList attachedVariations;
for (QObject *current = styleReader; current; current = current->parent()) {
if (const QObject *attachedObject = qmlAttachedPropertiesObject<QQStyleKitVariation>(current, false)) {
const auto *attached = static_cast<const QQStyleKitVariationAttached *>(attachedObject);
attachedVariations.append(attached);
}
}
parentObj = parentObj->parent();
if (attachedVariations.isEmpty())
return;
for (QQStyleKitStyle *current = style; current; current = current->fallbackStyle()) {
if (QQStyleKitTheme *theme = current->theme()) {
addInstanceVariationsToReader(styleReader, theme, attachedVariations);
addTypeVariationsToReader(styleReader, theme, attachedVariations);
}
addInstanceVariationsToReader(styleReader, current, attachedVariations);
addTypeVariationsToReader(styleReader, current, attachedVariations);
}
}
@@ -444,44 +452,39 @@ QVariant QQStyleKitPropertyResolver::readPropertyInRelevantControls(
return {};
}
QVariant QQStyleKitPropertyResolver::readPropertyInStyle(
QVariant QQStyleKitPropertyResolver::readPropertyInVariations(
const QList<QPointer<QQStyleKitVariation>> &variations,
const QQStyleKitStyleAndThemeBase *styleOrTheme,
const PropertyPathIds &ids,
const QQStyleKitExtendableControlType exactType,
const QList<QQStyleKitExtendableControlType> baseTypes,
const QQStyleKitStyle *style)
const QList<QQStyleKitExtendableControlType> baseTypes)
{
QVariant value;
while (true) {
value = readPropertyInRelevantControls(style->theme(), ids, exactType, baseTypes);
if (value.isValid())
break;
value = readPropertyInRelevantControls(style, ids, exactType, baseTypes);
if (value.isValid())
break;
if (auto *fallbackStyle = style->fallbackStyle()) {
/* Recurse into the fallback style, and search for the property there. If not
* found, and the fallback style has a fallback style, the recursion continues. */
fallbackStyle->syncFromQPalette(style->effectivePalette());
value = readPropertyInStyle(ids, exactType, baseTypes, fallbackStyle);
if (value.isValid())
bool foundAtLeastOneVariation = false;
for (const QPointer<QQStyleKitVariation> &variation : variations) {
if (!variation)
continue;
if (!variation->m_usageContext.contains(styleOrTheme)) {
if (foundAtLeastOneVariation) {
/* Optimization: We have found at least one effective variation in the list, but this
* one has a different usage context. This means that the remaining variations will also
* have a different usage context, since the variations were added to the list sorted on context.
* So we can end the iteration. */
break;
}
/* The variations list is sorted on usage context. And the first variation in the list with
* the correct usage context has yet to be found. So continue to the next variation. */
continue;
}
break;
foundAtLeastOneVariation = true;
const QVariant value = readPropertyInRelevantControls(variation, ids, exactType, baseTypes);
if (value.isValid())
return value;
}
if (Q_UNLIKELY(QQStyleKitDebug::enabled())) {
if (!value.isValid())
QQStyleKitDebug::notifyPropertyNotResolved(ids.property);
}
return value;
return {};
}
QVariant QQStyleKitPropertyResolver::readProperty(
const PropertyPathIds &ids, QQStyleKitReader *styleReader, QQStyleKitStyle *style)
QVariant QQStyleKitPropertyResolver::readPropertyInStyle(
QQStyleKitStyle *style, const PropertyPathIds &ids, QQStyleKitReader *styleReader)
{
/* Sync the palette of the style with the palette of the current reader. Note
* that this can cause palette bindings in the style to change, which will
@@ -502,27 +505,30 @@ QVariant QQStyleKitPropertyResolver::readProperty(
QVariant value;
while (true) {
for (const QPointer<QQStyleKitVariation> &variation : std::as_const(styleReader->m_effectiveInAppVariations)) {
if (!variation)
continue;
value = readPropertyInRelevantControls(variation, ids, exactType, baseTypes);
if (value.isValid())
break;
}
value = readPropertyInVariations(styleReader->m_effectiveVariations, style->theme(), ids, exactType, baseTypes);
if (value.isValid())
break;
for (const QPointer<QQStyleKitVariation> &variation : std::as_const(styleReader->m_effectiveInStyleVariations)) {
if (!variation)
continue;
value = readPropertyInRelevantControls(variation, ids, exactType, baseTypes);
if (value.isValid())
break;
}
value = readPropertyInRelevantControls(style->theme(), ids, exactType, baseTypes);
if (value.isValid())
break;
value = readPropertyInStyle(ids, exactType, baseTypes, style);
value = readPropertyInVariations(styleReader->m_effectiveVariations, style, ids, exactType, baseTypes);
if (value.isValid())
break;
value = readPropertyInRelevantControls(style, ids, exactType, baseTypes);
if (value.isValid())
break;
if (auto *fallbackStyle = style->fallbackStyle()) {
/* Recurse into the fallback style, and search for the property there. If not
* found, and the fallback style has a fallback style, the recursion continues. */
value = readPropertyInStyle(fallbackStyle, ids, styleReader);
if (value.isValid())
break;
}
break;
}
@@ -543,17 +549,16 @@ QVariant QQStyleKitPropertyResolver::readStyleProperty(
const QQSK::PropertyPathFlags pathFlags = group->pathFlags();
const QQSK::Subclass subclass = controlProperties->subclass();
if (subclass == QQSK::Subclass::QQStyleKitState) {
if (subclass != QQSK::Subclass::QQStyleKitReader) {
/* Due to propagation, we must know the readers state in order to resolve the value
* of a given property. For example, the background delegates color may follow a
* completely different lookup path—and therefore produce a different result—depending
* on whether the reader is a Slider or a Button, and whether it is hovered, pressed, etc.
* For this reason, when a property is accessed directly from a control (or one of its
* states) within the StyleKit, full propagation cannot be supported: the necessary
* reader context is unavailable.
* For this reason, when a property is not read via a QQStyleKitReader, propagation and
* fall back logic cannot be supported.
* However, to still allow static (i.e., non-propagating) bindings between properties
* inside a Style, we fall back to simply returning the value defined on the accessed
* control itself. */
* _inside_ a Style, we fall back to return the value set directly on the accessed control. */
Q_ASSERT(subclass == QQSK::Subclass::QQStyleKitState);
const QQStyleKitControlState *controlState = controlProperties->asQQStyleKitState();
const QQStyleKitControl *control = controlState->control();
const PropertyPathId propertyPathId = group->propertyPathId(property, PropertyPathId::Flag::IncludeSubtype);
@@ -575,57 +580,52 @@ QVariant QQStyleKitPropertyResolver::readStyleProperty(
return {};
}
if (subclass == QQSK::Subclass::QQStyleKitReader) {
QQStyleKitDebug::groupBeingRead = group;
QQStyleKitReader *styleReader = controlProperties->asQQStyleKitReader();
QQStyleKitDebug::groupBeingRead = group;
QQStyleKitReader *styleReader = controlProperties->asQQStyleKitReader();
if (s_isReadingProperty) {
if (!s_styleWarningsIssued) {
s_styleWarningsIssued = true;
qmlWarning(styleReader) << "The style property '" << property << "' was read "
<< "before finishing the read of another style property. "
<< "This is likely to cause a style glitch.";
}
if (s_isReadingProperty) {
if (!s_styleWarningsIssued) {
s_styleWarningsIssued = true;
qmlWarning(styleReader) << "The style property '" << property << "' was read "
<< "before finishing the read of another style property. "
<< "This is likely to cause a style glitch.";
}
QScopedValueRollback rollback(s_isReadingProperty, true);
}
QScopedValueRollback rollback(s_isReadingProperty, true);
PropertyPathIds ids;
ids.property = group->propertyPathId(property, PropertyPathId::Flag::ExcludeSubtype);
ids.alternative = group->propertyPathId(alternative, PropertyPathId::Flag::ExcludeSubtype);
const bool insideSubType = pathFlags &
(QQSK::PropertyPathFlag::DelegateSubtype1 | QQSK::PropertyPathFlag::DelegateSubtype2);
PropertyPathIds ids;
ids.property = group->propertyPathId(property, PropertyPathId::Flag::ExcludeSubtype);
ids.alternative = group->propertyPathId(alternative, PropertyPathId::Flag::ExcludeSubtype);
const bool insideSubType = pathFlags &
(QQSK::PropertyPathFlag::DelegateSubtype1 | QQSK::PropertyPathFlag::DelegateSubtype2);
if (insideSubType) {
ids.subTypeProperty = group->propertyPathId(property, PropertyPathId::Flag::IncludeSubtype);
ids.subTypeAlternative = group->propertyPathId(alternative, PropertyPathId::Flag::IncludeSubtype);
} else {
ids.subTypeProperty = PropertyPathId();
ids.subTypeAlternative = PropertyPathId();
}
if (!pathFlags.testFlag(QQSK::PropertyPathFlag::Global)) {
/* A style reader can have a storage that contains local property overrides (that is,
* interpolated values from an ongoing transition). When searching for a property, we
* therefore need to check this storage first. The exception is if the property was read
* inside the 'global' group, which means that we should read the values directly
* from the style. */
if (insideSubType) {
ids.subTypeProperty = group->propertyPathId(property, PropertyPathId::Flag::IncludeSubtype);
ids.subTypeAlternative = group->propertyPathId(alternative, PropertyPathId::Flag::IncludeSubtype);
} else {
ids.subTypeProperty = PropertyPathId();
ids.subTypeAlternative = PropertyPathId();
}
if (!pathFlags.testFlag(QQSK::PropertyPathFlag::Global)) {
/* A style reader can have a storage that contains local property overrides (that is,
* interpolated values from an ongoing transition). When searching for a property, we
* therefore need to check this storage first. The exception is if the property was read
* inside the 'global' group, which means that we should read the values directly
* from the style. */
if (insideSubType) {
const QVariant value = readPropertyInStorageForState(
ids.subTypeProperty, ids.subTypeAlternative, styleReader, QQSK::StateFlag::Normal);
if (value.isValid())
return value;
}
const QVariant value = readPropertyInStorageForState(
ids.property, ids.alternative, styleReader, QQSK::StateFlag::Normal);
ids.subTypeProperty, ids.subTypeAlternative, styleReader, QQSK::StateFlag::Normal);
if (value.isValid())
return value;
}
return readProperty(ids, styleReader, style);
const QVariant value = readPropertyInStorageForState(
ids.property, ids.alternative, styleReader, QQSK::StateFlag::Normal);
if (value.isValid())
return value;
}
Q_UNREACHABLE();
return {};
return readPropertyInStyle(style, ids, styleReader);
}
bool QQStyleKitPropertyResolver::writeStyleProperty(
@@ -25,9 +25,15 @@ QT_BEGIN_NAMESPACE
class QQStyleKitControl;
class QQStyleKitControls;
class QQStyleKitStyle;
class QQStyleKitStyleAndThemeBase;
class QQStyleKitReader;
class QQStyleKitPropertyGroup;
class QQStyleKitVariation;
class QQStyleKitVariationAttached;
// The stack-allocated capacity should be large enough to cover the maximum expected
// nesting depth of controls in the application, to avoid heap allocation.
using AttachedVariationList = QVarLengthArray<const QQStyleKitVariationAttached *, 20>;
class QQStyleKitPropertyResolver
{
Q_GADGET
@@ -89,29 +95,36 @@ private:
const QQStyleKitExtendableControlType exactType,
const QList<QQStyleKitExtendableControlType> baseTypes);
static QVariant readPropertyInStyle(
static QVariant readPropertyInVariations(
const QList<QPointer<QQStyleKitVariation>> &variations,
const QQStyleKitStyleAndThemeBase *styleOrTheme,
const PropertyPathIds &ids,
const QQStyleKitExtendableControlType exactType,
const QList<QQStyleKitExtendableControlType> baseTypes,
const QQStyleKitStyle *style);
const QList<QQStyleKitExtendableControlType> baseTypes);
static QVariant readProperty(
static QVariant readPropertyInStyle(
QQStyleKitStyle *style,
const PropertyPathIds &ids,
QQStyleKitReader *styleReader,
QQStyleKitStyle *style);
QQStyleKitReader *styleReader);
static void cacheReaderState(QQSK::State state);
static inline void addVariationToReader(
QQStyleKitReader *styleReader,
QQStyleKitStyleAndThemeBase *styleOrTheme,
QQStyleKitVariation *variation);
static void addInstanceVariationsToReader(
QQStyleKitReader *styleReader,
const QStringList &inAppVariationNames,
const QVarLengthArray<const QQStyleKitControls *, 6> &stylesAndThemes);
QQStyleKitStyleAndThemeBase *styleOrTheme,
const AttachedVariationList &attachedVariations);
static void addTypeVariationsToReader(
QQStyleKitReader *styleReader,
const QQStyleKitExtendableControlType parentType,
const QQStyleKitStyle *style);
static void rebuildVariationsForReader(
QQStyleKitReader *styleReader,
const QQStyleKitStyle *style);
QQStyleKitStyleAndThemeBase *styleOrTheme,
const AttachedVariationList &attachedVariations);
static void rebuildVariationsForReader(QQStyleKitReader *styleReader, QQStyleKitStyle *style);
};
QT_END_NAMESPACE
@@ -13,6 +13,215 @@
QT_BEGIN_NAMESPACE
/*!
\qmltype StyleReader
\inqmlmodule Qt.labs.StyleKit
\inherits ControlStyleProperties
\brief Reads properties from the active Style for a specific control.
StyleReader is the bridge between a control in \l {Qt Quick Controls}
and the \l {StyleKit::style}{active style}. It exposes all the style
properties — \l {ControlStyleProperties::background}{backgrounds},
\l {ControlStyleProperties::indicator}{indicators},
\l {ControlStyleProperties::handle}{handles},
\l {ControlStyleProperties::text}{text},
\l {ControlStyleProperties::padding}{padding},
and more — that a control, and its delegates, should bind to.
All built-in \c StyleKit controls use a StyleReader
internally for this purpose.
The state properties — \l hovered, \l pressed, \l focused, \l checked,
\l highlighted, etc. — tell StyleReader which state the control is in.
It uses these to resolve and read the correct property values from
the \l Style, taking \l {Theme}{Themes}, \l {StyleVariation}{StyleVariations},
\l {AbstractStylableControls}{fallback types}, and property propagation
into account.
When implementing a \l CustomControl, you can follow the same approach as
the built-in controls:
\snippet StyleReaderSnippets.qml custom control
\note The style properties inherited from \l ControlStyleProperties do not map
directly to the \l Style. Instead, they reflect a cache of potentially interpolated
values during an active \l {ControlStyle::}{transition}. Writing to them will \e not
update the corresponding properties in the \l Style and may interfere with ongoing
transitions. Write directly to the \l Style when you need to change a style property.
\labs
\sa CustomControl, Style, {StyleVariation::controlType}{StyleVariation.controlType}
*/
/*!
\qmlproperty enumeration StyleReader::controlType
Identifies which control type in the \l Style this reader reads
properties from. This can either be set to one of the predefined values
below, or to a \l {CustomControl::controlType}{custom control type} defined in the
\l Style.
\value StyleReader.Button \l {Button}
\value StyleReader.CheckBox \l {CheckBox}
\value StyleReader.ComboBox \l {ComboBox}
\value StyleReader.Frame \l {Frame}
\value StyleReader.GroupBox \l {GroupBox}
\value StyleReader.ItemDelegate \l {ItemDelegate}
\value StyleReader.Label \l {Label}
\value StyleReader.Menu \l {Menu}
\value StyleReader.Dialog \l {Dialog}
\value StyleReader.Page \l {Page}
\value StyleReader.Pane \l {Pane}
\value StyleReader.Popup \l {Popup}
\value StyleReader.ProgressBar \l {ProgressBar}
\value StyleReader.RadioButton \l {RadioButton}
\value StyleReader.ScrollBar \l {ScrollBar}
\value StyleReader.ScrollView \l {ScrollView}
\value StyleReader.SearchField \l {SearchField}
\value StyleReader.Slider \l {Slider}
\value StyleReader.SpinBox \l {SpinBox}
\value StyleReader.Switch \l {Switch}
\value StyleReader.TabBar \l {TabBar}
\value StyleReader.TabButton \l {TabButton}
\value StyleReader.TextArea \l {TextArea}
\value StyleReader.TextField \l {TextField}
\value StyleReader.ToolBar \l {ToolBar}
\value StyleReader.ToolButton \l {ToolButton}
\value StyleReader.ToolSeparator \l {ToolSeparator}
\sa {StyleVariation::controlType}{StyleVariation.controlType}
*/
/*!
\qmlproperty bool StyleReader::enabled
Whether the control is enabled.
Bind this to the control's \l {Item::enabled}{enabled} property.
The default value is \c true.
\sa {ControlStateStyle::disabled}{ControlStateStyle.disabled}
*/
/*!
\qmlproperty bool StyleReader::hovered
Whether the control is hovered.
Bind this to the control's \l {Control::hovered}{hovered} property.
The default value is \c false.
\sa {ControlStateStyle::hovered}{ControlStateStyle.hovered}
*/
/*!
\qmlproperty bool StyleReader::pressed
Whether the control is pressed.
Bind this to the control's \l {AbstractButton::pressed}{pressed} property.
The default value is \c false.
\sa {ControlStateStyle::pressed}{ControlStateStyle.pressed}
*/
/*!
\qmlproperty bool StyleReader::focused
Whether the control has active focus.
Bind this to the control's \l {Item::activeFocus}{activeFocus} property.
The default value is \c false.
\sa {ControlStateStyle::focused}{ControlStateStyle.focused}
*/
/*!
\qmlproperty bool StyleReader::checked
Whether the control is checked.
Bind this to the control's \l {AbstractButton::checked}{checked} property.
The default value is \c false.
\sa {ControlStateStyle::checked}{ControlStateStyle.checked}
*/
/*!
\qmlproperty bool StyleReader::highlighted
Whether the control is highlighted.
Bind this to the control's \l {Button::highlighted}{highlighted} property.
The default value is \c false.
\sa {ControlStateStyle::highlighted}{ControlStateStyle.highlighted}
*/
/*!
\qmlproperty bool StyleReader::vertical
Whether the control is oriented vertically.
Bind this to the control's \l {Slider::orientation}{orientation}.
The default value is \c false.
\sa {ControlStateStyle::vertical}{ControlStateStyle.vertical}
*/
/*!
\qmlproperty palette StyleReader::palette
The palette of the control. StyleKit uses this to resolve color properties
that bind the \l {Style::}{palette} in the \l Style.
Bind this to the control's \l {Control::palette}{palette} property.
\sa {Style::palette}{Style.palette}
*/
/*!
\qmlproperty font StyleReader::font
\readonly
The effective font for this control type, as defined by the
\l {AbstractStyle::fonts}{style}. This also takes into account any font
overrides set in the \l {ControlStyle::}{text} properties of the style.
Bind the control's \l {Control::font}{font} property to this property.
\note Unlike properties such as \l hovered, \l pressed and \l palette — which
is forwarded from the control to the StyleReader — \l font is an output. Bind the
control's \l {Control::font}{font} property to this value, not the other way around.
*/
/*!
\qmlproperty ControlStyleProperties StyleReader::global
\readonly
Provides direct access to the style properties, bypassing any ongoing
\l {ControlStyle::}{transition}.
While a state transition is ongoing, style properties read from a StyleReader
may return interpolated values. By prepending \c global to the property path,
you bypass the transition and get the end-state values immediately.
For example, when transitioning from \l hovered to \l pressed, \c {background.color}
may return an interpolated value between the \l {DelegateStyle::}{color} in the
\l {ControlStateStyle::}{hovered} state and the color in the
\l {ControlStateStyle::}{pressed} state. \c {global.background.color}, on the
other hand, returns the color in the pressed state directly.
\sa {StyleKit::transitionsEnabled}{transitionsEnabled}
*/
using namespace Qt::StringLiterals;
static const QString kAlternate1 = "A1"_L1;
@@ -53,17 +262,7 @@ static quint64 textFontOverridesSignature(const QQStyleKitTextProperties *t)
QList<QQStyleKitReader *> QQStyleKitReader::s_allReaders;
QMap<QString, QQmlComponent *> QQStyleKitReader::s_propertyChangesComponents;
/*!
\qmltype StyleReader
\inqmlmodule Qt.labs.StyleKit
\brief Provides access to control state and palette for styling.
\labs
*/
/*!
\qmlproperty palette StyleReader::palette
*/
QQStyleKitReader::QQStyleKitReader(QObject *parent)
: QQStyleKitControlProperties(QQSK::PropertyGroup::Control, parent)
@@ -110,9 +309,9 @@ QQuickStateGroup *QQStyleKitReader::stateGroup()
QQmlComponent *QQStyleKitReader::createControlChangesComponent() const
{
static const QLatin1String propertyName("control"_L1);
if (s_propertyChangesComponents.contains(propertyName))
return s_propertyChangesComponents.value(propertyName);
const auto key = u"control"_s;
if (auto r = s_propertyChangesComponents.value(key, nullptr))
return r;
const QString qmlControlCode = QString::fromUtf8(R"(
import QtQuick
@@ -140,14 +339,14 @@ QQmlComponent *QQStyleKitReader::createControlChangesComponent() const
QQmlComponent *component = new QQmlComponent(qmlEngine(style()));
component->setData(qmlControlCode.toUtf8(), QUrl());
Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
s_propertyChangesComponents.insert(propertyName, component);
s_propertyChangesComponents.insert(key, component);
return component;
}
QQmlComponent *QQStyleKitReader::createDelegateChangesComponent(const QString &delegateName) const
{
if (s_propertyChangesComponents.contains(delegateName))
return s_propertyChangesComponents.value(delegateName);
if (auto r = s_propertyChangesComponents.value(delegateName, nullptr))
return r;
static const QString qmlTemplateCode = QString::fromUtf8(R"(
import QtQuick
@@ -29,7 +29,7 @@ class QQuickPalette;
class QQStyleKitVariation;
class QQStyleKitPropertyResolver;
class QQStyleKitReader : public QQStyleKitControlProperties
class Q_LABSSTYLEKIT_EXPORT QQStyleKitReader : public QQStyleKitControlProperties
{
Q_OBJECT
Q_PROPERTY(QQStyleKitExtendableControlType controlType READ controlType WRITE setControlType NOTIFY controlTypeChanged FINAL)
@@ -197,8 +197,7 @@ private:
QQSK::Delegates m_trackedDelegates = QQSK::Delegate::NoDelegate;
QPointer<QQStyleKitReader> m_parentReader;
QList<QPointer<QQStyleKitVariation>> m_effectiveInAppVariations;
QList<QPointer<QQStyleKitVariation>> m_effectiveInStyleVariations;
QList<QPointer<QQStyleKitVariation>> m_effectiveVariations;
QQStyleKitControlProperties m_global;
@@ -4,6 +4,7 @@
#include "qqstylekit_p.h"
#include "qqstylekitstyle_p.h"
#include "qqstylekittheme_p.h"
#include "qqstylekitvariation_p.h"
#include "qqstylekitcustomtheme_p.h"
#include "qqstylekitcontrolproperties_p.h"
#include "qqstylekitpropertyresolver_p.h"
@@ -51,7 +52,7 @@ QT_BEGIN_NAMESPACE
\labs
\sa Theme, CustomTheme, StyleVariation, ControlStyle, DelegateStyle,
CustomControl, {qtlabsstylekit-property-resolution.html}{Property Resolution}
CustomControl
*/
/*!
@@ -91,7 +92,7 @@ QT_BEGIN_NAMESPACE
\snippet StyleSnippets.qml dark
\sa light, themeName, {qtlabsstylekit-theme.html}{Theme}
\sa light, themeName
*/
/*!
@@ -110,8 +111,6 @@ QT_BEGIN_NAMESPACE
you to set many more properties than otherwise needed. A
reference implementation of a fallback style can be found
\l {qtlabsstylekit-fallbackstyle.html}{here.}
\sa {qtlabsstylekit-property-resolution.html}{Property Resolution}
*/
/*!
@@ -123,7 +122,7 @@ QT_BEGIN_NAMESPACE
\snippet StyleSnippets.qml light
\sa dark, themeName, {qtlabsstylekit-theme.html}{Theme}
\sa dark, themeName
*/
/*!
@@ -337,7 +336,8 @@ void QQStyleKitStyle::setThemeName(const QString &themeName)
return;
m_themeName = themeName;
recreateTheme();
if (m_completed)
recreateTheme();
emit themeNameChanged();
}
@@ -377,18 +377,15 @@ void QQStyleKitStyle::recreateTheme()
qmlWarning(this) << "Custom theme '" << effectiveThemeName << "' has no theme component set";
}
if (m_effectiveThemeName == effectiveThemeName) {
// Switching theme name from e.g "System" to "Light" might not
// actually change the currently effective theme.
emit themeNameChanged();
if (m_effectiveThemeName == effectiveThemeName)
return;
}
if (m_theme) {
m_theme->deleteLater();
m_theme = nullptr;
}
m_effectiveThemeName = effectiveThemeName;
m_currentThemeComponent = effectiveThemeComponent;
if (effectiveThemeComponent) {
@@ -426,9 +423,11 @@ void QQStyleKitStyle::recreateTheme()
m_theme->fonts()->setFallbackFont(fonts());
if (m_theme->palettes())
m_theme->palettes()->setFallbackPalette(palettes());
if (this == current()) {
m_theme->updateThemePalettes();
m_theme->updateThemeFonts();
QQStyleKitVariation::resetVariationsForStyle(this);
QQStyleKitReader::resetAll();
}
@@ -510,4 +509,3 @@ void QQStyleKitStyle::componentComplete()
QT_END_NAMESPACE
#include "moc_qqstylekitstyle_p.cpp"
@@ -107,6 +107,7 @@ private:
bool m_completed = false;
bool m_isUpdatingPalette = false;
bool m_hasVariations = false;
QQuickDeferredPointer<QQStyleKitStyle> m_fallbackStyle;
QPointer<QQmlComponent> m_light;
@@ -124,6 +125,8 @@ private:
friend class QQStyleKitAttached;
friend class QQStyleKitPropertyGroup;
friend class QQStyleKitPropertyResolver;
friend class QQStyleKitDebug;
friend class QQStyleKitVariation;
};
QT_END_NAMESPACE
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqstylekitstyleandthemebase_p.h"
#include "qqstylekitvariation_p.h"
QT_BEGIN_NAMESPACE
@@ -23,6 +23,8 @@
QT_BEGIN_NAMESPACE
class QQStyleKitVariation;
class QQStyleKitStyleAndThemeBase : public QQStyleKitControls
{
Q_OBJECT
@@ -41,12 +43,17 @@ signals:
void fontsChanged();
void palettesChanged();
private:
protected:
QList<QQStyleKitVariation *> m_styleVariations;
private:
QQStyleKitFont m_fonts;
QQStyleKitPalette m_palettes;
Q_DISABLE_COPY(QQStyleKitStyleAndThemeBase)
friend class QQStyleKitPropertyResolver;
friend class QQStyleKitVariation;
};
QT_END_NAMESPACE
@@ -133,14 +133,15 @@ void QQStyleKitTheme::updateThemePalettes()
// Apply from farthest fallback -> local
for (int i = fbChain.size() - 1; i >= 0; --i) {
const QQStyleKitPalette *fb = fbChain[i];
if (fb->isSet(scope)) {
if (auto *p = (fb->*getter)())
result = p->toQPalette().resolve(result);
}
// 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;
@@ -185,12 +186,13 @@ void QQStyleKitTheme::updateThemeFonts()
// Apply from farthest fallback -> local
for (int i = fbChain.size() - 1; i >= 0; --i) {
const QQStyleKitFont *fb = fbChain[i];
if (fb->isSet(scope)) {
result = fb->fontForScope(scope).resolve(result);
}
// Apply system font first as a base, and override with scope-specific font
if (scope != QQuickTheme::System && fb->isSet(QQuickTheme::System)) {
result = fb->fontForScope(QQuickTheme::System).resolve(result);
}
if (fb->isSet(scope)) {
result = fb->fontForScope(scope).resolve(result);
}
}
return result;
@@ -2,6 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqstylekitvariation_p.h"
#include "qqstylekitstyleandthemebase_p.h"
#include "qqstylekitstyle_p.h"
#include "qqstylekittheme_p.h"
QT_BEGIN_NAMESPACE
@@ -122,28 +125,43 @@ QT_BEGIN_NAMESPACE
/*!
\qmlattachedproperty int StyleVariation::controlType
This property identifies the control type of the item it is attached to.
This property identifies the \l {StyleReader::controlType}{control type} of
the item it is attached to.
StyleKit uses it to resolve \l {Type Variations}{type variations} for descendant
controls — if a parent item's \c controlType matches a control type that has
\l {ControlStyle::}{variations} defined in the \l Style, those variations apply
to all descendant controls.
Built-in StyleKit controls set this property automatically. Custom controls
Built-in controls set this property automatically. Custom controls
must set it explicitly to participate in type variation resolution.
\sa {ControlStyle::variations}{ControlStyle.variations}, StyleReader
\sa {ControlStyle::variations}{ControlStyle.variations},
{StyleReader::controlType}{StyleReader.controlType}
*/
int QQStyleKitVariation::s_typeVariationCount = 0;
int QQStyleKitVariationAttached::s_instanceVariationCount = 0;
QQStyleKitVariation::QQStyleKitVariation(QObject *parent)
: QQStyleKitControls(parent)
{
/* As an optimization, keep track of how many type variations that are defined
* inside the style. That way we can skip looking for them later if s_typeVariationCount == 0. */
++s_typeVariationCount;
}
void QQStyleKitVariation::componentComplete()
{
QQStyleKitControls::componentComplete();
/* Whenever there is a Style or Theme change, the list of StyleVariations that affects a
* StyleReader needs to be rebuilt. And in order to know which StyleVariations that should
* be taken into consideration, we need to know which Style each StyleVariation belongs to. */
bool styleOrThemeFound = false;
for (QObject *current = parent(); current; current = current->parent()) {
if (auto *styleOrTheme = qobject_cast<QQStyleKitStyleAndThemeBase *>(current)) {
styleOrTheme->m_styleVariations.append(this);
styleOrThemeFound = true;
break;
}
}
if (!styleOrThemeFound)
qmlWarning(this) << "A StyleVariation needs to be a descendant of the Style it belongs to!";
}
QQStyleKitVariationAttached *QQStyleKitVariation::qmlAttachedProperties(QObject *object)
@@ -180,16 +198,11 @@ void QQStyleKitVariationAttached::setVariations(const QStringList &variations)
if (m_variations == variations)
return;
/* As an optimization, we count the number of instance variations set from the application.
* That way, if s_instanceVariationCount == 1, for example, and we found a variation while
* resolving the effective variations for a specific QQStyleReader, we can stop the search. */
s_instanceVariationCount++;
m_variations = variations;
emit variationsChanged();
}
QQStyleKitExtendableControlType QQStyleKitVariationAttached::controlType()
QQStyleKitExtendableControlType QQStyleKitVariationAttached::controlType() const
{
return m_controlType;
}
@@ -203,6 +216,39 @@ void QQStyleKitVariationAttached::setControlType(QQStyleKitExtendableControlType
emit controlTypeChanged();
}
void QQStyleKitVariation::resetVariationsForStyle(QQStyleKitStyle *style)
{
/* The usage context stored in a StyleVariation tells which Style or Theme uses it.
* After a theme change, we therefore need to rebuild this context list, since the
* old theme needs to be removed, and the new theme might need to be added.
* Since we also need to update which variations will now be effective for the existing
* StyleReaders, we simply clear the context here, and leave it to QQStyleKitPropertyResolver
* to rebuild both the effective variations and the context in one pass later.
* (QQStyleKitReader::resetReadersForStyle() will set the m_effectiveVariationsDirty
* flag, which will trigger the rebuild).
*
* As an optimization, we also update m_hasVariations to reflect whether any variations
* exist at all. If not, variations don't need to be taken into consideration during
* style property look-up. */
style->m_hasVariations = false;
for (QQStyleKitStyle *current = style; current; current = current->fallbackStyle()) {
if (!current->m_styleVariations.isEmpty()) {
style->m_hasVariations = true;
for (QQStyleKitVariation *variation : current->m_styleVariations)
variation->m_usageContext.clear();
}
if (QQStyleKitTheme *theme = current->theme()) {
if (!theme->m_styleVariations.isEmpty()) {
style->m_hasVariations = true;
for (QQStyleKitVariation *variation : theme->m_styleVariations)
variation->m_usageContext.clear();
}
}
}
}
QT_END_NAMESPACE
#include "moc_qqstylekitvariation_p.cpp"
@@ -22,6 +22,8 @@
QT_BEGIN_NAMESPACE
class QQStyleKitVariationAttached;
class QQStyleKitStyleAndThemeBase;
class QQStyleKitStyle;
class QQStyleKitVariation : public QQStyleKitControls
{
@@ -33,6 +35,8 @@ class QQStyleKitVariation : public QQStyleKitControls
public:
QQStyleKitVariation(QObject *parent = nullptr);
void componentComplete() override;
QString name() const;
void setName(const QString &name);
@@ -44,11 +48,13 @@ Q_SIGNALS:
private:
Q_DISABLE_COPY(QQStyleKitVariation)
QString m_name;
static void resetVariationsForStyle(QQStyleKitStyle *style);
static int s_typeVariationCount;
QString m_name;
QList<QPointer<const QQStyleKitStyleAndThemeBase>> m_usageContext;
friend class QQStyleKitPropertyResolver;
friend class QQStyleKitStyle;
};
class QQStyleKitVariationAttached : public QObject
@@ -63,7 +69,7 @@ public:
QStringList variations() const;
void setVariations(const QStringList &variations);
QQStyleKitExtendableControlType controlType();
QQStyleKitExtendableControlType controlType() const;
void setControlType(QQStyleKitExtendableControlType type);
signals:
@@ -74,8 +80,6 @@ public:
QStringList m_variations;
QQStyleKitExtendableControlType m_controlType = QQStyleKitReader::ControlType::Unspecified;
static int s_instanceVariationCount;
friend class QQStyleKit;
friend class QQStyleKitPropertyResolver;
};
@@ -94,6 +94,7 @@ public:
\table
\row
\li \image qtlabs-wavefrontmesh.png
{Torus shape with gradient texture coordinates}
\li \qml
import QtQuick 2.\1
import Qt.labs.wavefrontmesh 1.\1
@@ -1,6 +1,7 @@
// Copyright (C) 2016 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 <QtCore/qtconfigmacros.h>
#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "qquickcumulativedirection_p.h"
@@ -1,6 +1,7 @@
// Copyright (C) 2021 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 <QtCore/qtconfigmacros.h>
#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include <QtQuick/private/qsgcontext_p.h>
@@ -509,13 +510,13 @@ void fillUniformArrayFromImage(float* array, const QImage& img, int size)
\table
\row
\li qrc:///particleresources/star.png
\li \inlineimage particles/star.png
\li \inlineimage particles/star.png {White star particle}
\row
\li qrc:///particleresources/glowdot.png
\li \inlineimage particles/glowdot.png
\li \inlineimage particles/glowdot.png {White glowing dot particle}
\row
\li qrc:///particleresources/fuzzydot.png
\li \inlineimage particles/fuzzydot.png
\li \inlineimage particles/fuzzydot.png {White fuzzy dot particle}
\endtable
Note that the images are white and semi-transparent, to allow colorization
@@ -170,7 +170,7 @@ class Q_QUICKPARTICLES_EXPORT QQuickImageParticle : public QQuickParticlePainter
Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged RESET resetColor)
Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged RESET resetColor)
Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged RESET resetRotation)
Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged RESET resetRotation OVERRIDE)
Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged RESET resetRotation)
Q_PROPERTY(qreal rotationVelocity READ rotationVelocity WRITE setRotationVelocity NOTIFY rotationVelocityChanged RESET resetRotation)
Q_PROPERTY(qreal rotationVelocityVariation READ rotationVelocityVariation WRITE setRotationVelocityVariation NOTIFY rotationVelocityVariationChanged RESET resetRotation)
@@ -1,9 +1,11 @@
// Copyright (C) 2016 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 <QtCore/qtconfigmacros.h>
#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "qquickitemparticle_p.h"
#include "qquickparticleemitter_p.h"
#include <QtQuick/qsgnode.h>
#include <QTimer>
#include <QQmlComponent>
@@ -293,9 +295,14 @@ QQuickItemParticleAttached *QQuickItemParticle::qmlAttachedProperties(QObject *o
bool QQuickItemParticle::clockShouldUpdate() const
{
QQuickItem *parentItem = qobject_cast<QQuickItem *>(parent());
return (m_system && m_system->isRunning() && !m_system->isPaused() && m_system->isEnabled()
&& ((parentItem && parentItem->isEnabled()) || !parentItem) && isEnabled());
// Determine if the QQuickItemParticle clock should stop by looking at the system running
// state and any parent Emitter enabled state. Note that this enabled state is the overridden
// QQuickParticleEmitter::enabled, and not the unrelated QQuickItem::enabled (which controls
// if the item reacts to input events).
QQuickParticleEmitter *parentEmitter = qobject_cast<QQuickParticleEmitter *>(parent());
bool parentEmitterDisabled = parentEmitter && !parentEmitter->isEnabled();
bool systemRunning = m_system && (m_system->isRunning() || m_system->isPaused());
return systemRunning && !parentEmitterDisabled;
}
void QQuickItemParticle::reconnectParent(QQuickItem *parentItem)
@@ -1,6 +1,7 @@
// Copyright (C) 2016 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 <QtCore/qtconfigmacros.h>
#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "qquickparticleaffector_p.h"
@@ -29,7 +29,7 @@ class Q_QUICKPARTICLES_EXPORT QQuickParticleAffector : public QQuickItem
Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
Q_PROPERTY(QStringList whenCollidingWith READ whenCollidingWith WRITE setWhenCollidingWith NOTIFY whenCollidingWithChanged)
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged OVERRIDE)
Q_PROPERTY(bool once READ onceOff WRITE setOnceOff NOTIFY onceChanged)
Q_PROPERTY(QQuickParticleExtruder* shape READ shape WRITE setShape NOTIFY shapeChanged)
@@ -1,6 +1,7 @@
// Copyright (C) 2016 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 <QtCore/qtconfigmacros.h>
#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "qquickparticleemitter_p.h"
@@ -34,7 +34,7 @@ class Q_QUICKPARTICLES_EXPORT QQuickParticleEmitter : public QQuickItem
Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
Q_PROPERTY(QString group READ group WRITE setGroup NOTIFY groupChanged)
Q_PROPERTY(QQuickParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged)
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged OVERRIDE)
Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(qreal emitRate READ particlesPerSecond WRITE setParticlesPerSecond NOTIFY particlesPerSecondChanged)
@@ -1,6 +1,7 @@
// Copyright (C) 2016 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 <QtCore/qtconfigmacros.h>
#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "qquickparticlegroup_p.h"

Some files were not shown because too many files have changed in this diff Show More