diff options
author | Mitch Curtis <[email protected]> | 2022-06-23 15:33:34 +0800 |
---|---|---|
committer | Mitch Curtis <[email protected]> | 2022-10-13 11:34:11 +0800 |
commit | 49b9f1f1e85ce9aee960b1384fb542daacd8b7a7 (patch) | |
tree | 575cebdd0dd014d6569bfec36f20dbe5f6cddf4b | |
parent | 5eb52b725526c26d0efa3856b6aba1919010f8fd (diff) |
Make QQuickAttachedPropertyPropagator public
This type has been used internally as QQuickAttachedObject by the
Imagine, Material and Universal styles to enable propagation of colors,
dark mode flags, etc. for a while now. Users would benefit from having
access to it to create their own styles (although it's not just limited
to that use case).
This patch:
- Makes the type public in quickcontrols2.
- Adds documentation and an example.
- Fixes the test_window test to ensure that propagation through child
windows actually works.
[ChangeLog][Controls] Added QQuickAttachedPropertyPropagator, which
provides a way to propagate attached properties from parent objects
to children. This is especially useful when creating your own style.
Fixes: QTBUG-63267
Change-Id: I2f1794dc4a9f2be56fad2f5e5f39e2ab845157fa
Reviewed-by: Fabian Kosmale <[email protected]>
Reviewed-by: Richard Moe Gustavsen <[email protected]>
31 files changed, 1305 insertions, 363 deletions
diff --git a/examples/quickcontrols2/CMakeLists.txt b/examples/quickcontrols2/CMakeLists.txt index 3473052f68..e4021bf430 100644 --- a/examples/quickcontrols2/CMakeLists.txt +++ b/examples/quickcontrols2/CMakeLists.txt @@ -1,6 +1,7 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +qt_internal_add_example(attachedstyleproperties) qt_internal_add_example(gallery) if (TARGET Qt::Sql) add_subdirectory(chattutorial) diff --git a/examples/quickcontrols2/attachedstyleproperties/CMakeLists.txt b/examples/quickcontrols2/attachedstyleproperties/CMakeLists.txt new file mode 100644 index 0000000000..229129f5c6 --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.16) +project(attachedstyleproperties LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols2/attachedstyleproperties") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml QuickControls2) +qt_standard_project_setup() + +add_subdirectory(MyStyle) + +qt_add_executable(attachedstylepropertiesexample + WIN32 + MACOSX_BUNDLE + attachedstyleproperties.cpp +) + +qt_add_qml_module(attachedstylepropertiesexample + URI App + VERSION 1.0 + QML_FILES + attachedstyleproperties.qml + NO_RESOURCE_TARGET_PATH +) + +target_link_libraries(attachedstylepropertiesexample PRIVATE + Qt::Core + Qt::Gui + Qt::Qml + Qt::QuickControls2 +) + +add_dependencies(attachedstylepropertiesexample MyStyle) + +install(TARGETS attachedstylepropertiesexample + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) + +include(../../quick/shared/QtBundleQmlModuleForMacOS.cmake) + +set(app_target "attachedstylepropertiesexample") +set(qml_plugin_target "MyStyleplugin") +set(qml_module_uri "MyStyle") +add_qml_module_to_macos_app_bundle( + "${app_target}" "${qml_plugin_target}" "${qml_module_uri}") diff --git a/examples/quickcontrols2/attachedstyleproperties/MyStyle/ApplicationWindow.qml b/examples/quickcontrols2/attachedstyleproperties/MyStyle/ApplicationWindow.qml new file mode 100644 index 0000000000..8a7bbc7ed2 --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/MyStyle/ApplicationWindow.qml @@ -0,0 +1,17 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Templates as T + +import MyStyle + +T.ApplicationWindow { + color: MyStyle.windowColor + + Behavior on color { + ColorAnimation { + duration: 150 + } + } +} diff --git a/examples/quickcontrols2/attachedstyleproperties/MyStyle/Button.qml b/examples/quickcontrols2/attachedstyleproperties/MyStyle/Button.qml new file mode 100644 index 0000000000..7eabcb0b35 --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/MyStyle/Button.qml @@ -0,0 +1,33 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Templates as T + +import MyStyle + +T.Button { + id: control + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + + padding: 6 + horizontalPadding: padding + 2 + spacing: 6 + + contentItem: Text { + text: control.text + font: control.font + color: control.MyStyle.buttonTextColor + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + implicitWidth: 60 + implicitHeight: 40 + color: control.down ? Qt.darker(control.MyStyle.buttonColor, 1.1) : control.MyStyle.buttonColor + } +} diff --git a/examples/quickcontrols2/attachedstyleproperties/MyStyle/CMakeLists.txt b/examples/quickcontrols2/attachedstyleproperties/MyStyle/CMakeLists.txt new file mode 100644 index 0000000000..310670677e --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/MyStyle/CMakeLists.txt @@ -0,0 +1,47 @@ +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + message(FATAL_ERROR "MyStyle should be built as part of the 'attachedstyleproperties' project, and not in isolation.") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols2/attachedstyleproperties") + +qt_add_qml_module(MyStyle + URI MyStyle + VERSION 1.0 + IMPORTS + QtQuick.Controls.Material + QML_FILES + ApplicationWindow.qml + Button.qml + Label.qml + Popup.qml + Switch.qml + ToolBar.qml + SOURCES + mystyle.cpp + mystyle.h +) + +include(GenerateExportHeader) +target_include_directories(MyStyle PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # find autogenerated header +generate_export_header(MyStyle) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml QuickControls2) + +target_link_libraries(MyStyle PRIVATE + Qt::Core + Qt::Gui + Qt::Qml + Qt::QuickControls2 +) + +message(STATUS "@@@ INSTALL_EXAMPLEDIR ${INSTALL_EXAMPLEDIR}") + +install(TARGETS MyStyle + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir + DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/quickcontrols2/attachedstyleproperties/MyStyle/Label.qml b/examples/quickcontrols2/attachedstyleproperties/MyStyle/Label.qml new file mode 100644 index 0000000000..3d045aa70e --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/MyStyle/Label.qml @@ -0,0 +1,11 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Templates as T + +import MyStyle + +T.Label { + color: MyStyle.windowTextColor +} diff --git a/examples/quickcontrols2/attachedstyleproperties/MyStyle/Popup.qml b/examples/quickcontrols2/attachedstyleproperties/MyStyle/Popup.qml new file mode 100644 index 0000000000..0f5e753f60 --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/MyStyle/Popup.qml @@ -0,0 +1,48 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Templates as T +import MyStyle + +T.Popup { + id: control + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + contentHeight + topPadding + bottomPadding) + + padding: 12 + + enter: Transition { + NumberAnimation { + property: "opacity" + from: 0.0 + to: 1.0 + easing.type: Easing.OutCubic + duration: 150 + } + } + + exit: Transition { + NumberAnimation { + property: "opacity" + from: 1.0 + to: 0.0 + easing.type: Easing.OutCubic + duration: 150 + } + } + + background: Rectangle { + radius: 2 + color: control.MyStyle.popupColor + border.color: control.MyStyle.popupBorderColor + } + + T.Overlay.modeless: Rectangle { + color: control.MyStyle.backgroundDimColor + Behavior on opacity { NumberAnimation { duration: 150 } } + } +} diff --git a/examples/quickcontrols2/attachedstyleproperties/MyStyle/Switch.qml b/examples/quickcontrols2/attachedstyleproperties/MyStyle/Switch.qml new file mode 100644 index 0000000000..59bda147b7 --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/MyStyle/Switch.qml @@ -0,0 +1,58 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Templates as T + +import MyStyle + +T.Switch { + id: control + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + + padding: 6 + horizontalPadding: padding + 2 + spacing: 6 + + indicator: Rectangle { + implicitWidth: 56 + implicitHeight: 28 + + x: control.text + ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) + : control.leftPadding + (control.availableWidth - width) / 2 + y: control.topPadding + (control.availableHeight - height) / 2 + + radius: 8 + color: control.down ? Qt.darker(control.MyStyle.buttonColor, 1.1) : control.MyStyle.buttonColor + + Rectangle { + x: Math.max(0, Math.min(parent.width - width, control.visualPosition * parent.width - (width / 2))) + y: (parent.height - height) / 2 + width: 28 + height: 28 + radius: 8 + color: Qt.lighter(control.MyStyle.buttonColor) + + Behavior on x { + enabled: !control.down + SmoothedAnimation { velocity: 200 } + } + } + } + + contentItem: Text { + leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0 + rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0 + + text: control.text + font: control.font + color: control.MyStyle.windowTextColor + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } +} diff --git a/examples/quickcontrols2/attachedstyleproperties/MyStyle/ToolBar.qml b/examples/quickcontrols2/attachedstyleproperties/MyStyle/ToolBar.qml new file mode 100644 index 0000000000..b0cd597c87 --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/MyStyle/ToolBar.qml @@ -0,0 +1,24 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Templates as T + +import MyStyle + +T.ToolBar { + id: control + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + contentHeight + topPadding + bottomPadding) + + background: Rectangle { + implicitHeight: 40 + // Ensure that we use Control's attached MyStyle object by qualifying + // the binding with its id. If we don't do this, an extra, unnecessary + // attached MyStyle object will be created for the Rectangle. + color: control.MyStyle.toolBarColor + } +} diff --git a/examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.cpp b/examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.cpp new file mode 100644 index 0000000000..40f7af702e --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.cpp @@ -0,0 +1,136 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mystyle.h" + +// If no value was inherited from a parent or explicitly set, the "global" values are used. +static MyStyle::Theme globalTheme = MyStyle::Light; + +MyStyle::MyStyle(QObject *parent) + : QQuickAttachedPropertyPropagator(parent) + , m_theme(globalTheme) +{ + // A static function could be called here that reads globalTheme from a + // settings file once at startup. That value would override the global + // value. This is similar to what the Imagine and Material styles do, for + // example. + + initialize(); +} + +MyStyle *MyStyle::qmlAttachedProperties(QObject *object) +{ + return new MyStyle(object); +} + +MyStyle::Theme MyStyle::theme() const +{ + return m_theme; +} + +void MyStyle::setTheme(Theme theme) +{ + // When this function is called, we know that the user has explicitly + // set a theme on this attached object. We set this to true even if + // the effective theme didn't change, because it's important that + // the user's specified value is respected (and not inherited from + // from the parent). + m_explicitTheme = true; + if (m_theme == theme) + return; + + m_theme = theme; + propagateTheme(); + themeChange(); + +} + +void MyStyle::inheritTheme(Theme theme) +{ + if (m_explicitTheme || m_theme == theme) + return; + + m_theme = theme; + propagateTheme(); + themeChange(); +} + +void MyStyle::propagateTheme() +{ + const auto styles = attachedChildren(); + for (QQuickAttachedPropertyPropagator *child : styles) { + MyStyle *myStyle = qobject_cast<MyStyle *>(child); + if (myStyle) + myStyle->inheritTheme(m_theme); + } +} + +void MyStyle::resetTheme() +{ + if (!m_explicitTheme) + return; + + m_explicitTheme = false; + MyStyle *myStyle = qobject_cast<MyStyle *>(attachedParent()); + inheritTheme(myStyle ? myStyle->theme() : globalTheme); +} + +void MyStyle::themeChange() +{ + emit themeChanged(); + // Emit any other change signals for properties that depend on the theme here... +} + +QColor MyStyle::windowColor() const +{ + return m_theme == Light ? QColor::fromRgb(0xf0f0f0) : QColor::fromRgb(0x303030); +} + +QColor MyStyle::windowTextColor() const +{ + return m_theme == Light ? QColor::fromRgb(0x5c5c5c) : QColor::fromRgb(0xe0e0e0); +} + +QColor MyStyle::buttonColor() const +{ + return m_theme == Light ? QColor::fromRgb(0xc2e1ff) : QColor::fromRgb(0x74bbff); +} + +QColor MyStyle::buttonTextColor() const +{ + return m_theme == Light ? QColor::fromRgb(0x5c5c5c) : QColor::fromRgb(0xffffff); +} + +QColor MyStyle::toolBarColor() const +{ + return m_theme == Light ? QColor::fromRgb(0x4da6ff) : QColor::fromRgb(0x0066cc); +} + +QColor MyStyle::popupColor() const +{ +// const QColor winColor = windowColor(); +// return m_theme == Light ? winColor.darker(120) : winColor.lighter(120); + return windowColor().lighter(120); +} + +QColor MyStyle::popupBorderColor() const +{ + const QColor winColor = windowColor(); + return m_theme == Light ? winColor.darker(140) : winColor.lighter(140); +} + +QColor MyStyle::backgroundDimColor() const +{ + const QColor winColor = windowColor().darker(); + return QColor::fromRgb(winColor.red(), winColor.green(), winColor.blue(), 100); +} + +void MyStyle::attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) +{ + Q_UNUSED(oldParent); + MyStyle *attachedParentStyle = qobject_cast<MyStyle *>(newParent); + if (attachedParentStyle) { + inheritTheme(attachedParentStyle->theme()); + // Do any other inheriting here... + } +} diff --git a/examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.h b/examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.h new file mode 100644 index 0000000000..da87ab4718 --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.h @@ -0,0 +1,76 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef MYSTYLE_H +#define MYSTYLE_H + +#include <QColor> +#include <QQmlEngine> +#include <QQuickAttachedPropertyPropagator> + +#include "mystyle_export.h" + +class MYSTYLE_EXPORT MyStyle : public QQuickAttachedPropertyPropagator +{ + Q_OBJECT + // Provide a RESET function in order to allow an item to set MyStyle.theme to undefined + // in order to use its parent's (or global) theme after one was explicitly set on it. + Q_PROPERTY(Theme theme READ theme WRITE setTheme RESET resetTheme NOTIFY themeChanged FINAL) + // As the values of these properties only depend on the theme, they can all use the theme + // property's change signal. + Q_PROPERTY(QColor windowColor READ windowColor NOTIFY themeChanged FINAL) + Q_PROPERTY(QColor windowTextColor READ windowTextColor NOTIFY themeChanged FINAL) + Q_PROPERTY(QColor buttonColor READ buttonColor NOTIFY themeChanged FINAL) + Q_PROPERTY(QColor buttonTextColor READ buttonTextColor NOTIFY themeChanged FINAL) + Q_PROPERTY(QColor toolBarColor READ toolBarColor NOTIFY themeChanged FINAL) + Q_PROPERTY(QColor popupColor READ popupColor NOTIFY themeChanged FINAL) + Q_PROPERTY(QColor popupBorderColor READ popupBorderColor NOTIFY themeChanged FINAL) + Q_PROPERTY(QColor backgroundDimColor READ backgroundDimColor NOTIFY themeChanged FINAL) + + QML_ELEMENT + QML_ATTACHED(MyStyle) + QML_UNCREATABLE("") + QML_ADDED_IN_VERSION(1, 0) + +public: + enum Theme { + Light, + Dark + }; + + Q_ENUM(Theme) + + explicit MyStyle(QObject *parent = nullptr); + + static MyStyle *qmlAttachedProperties(QObject *object); + + Theme theme() const; + void setTheme(Theme theme); + void inheritTheme(Theme theme); + void propagateTheme(); + void resetTheme(); + void themeChange(); + + QColor windowColor() const; + QColor windowTextColor() const; + QColor buttonColor() const; + QColor buttonTextColor() const; + QColor toolBarColor() const; + QColor popupColor() const; + QColor popupBorderColor() const; + QColor backgroundDimColor() const; + +Q_SIGNALS: + void themeChanged(); + +protected: + void attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) override; + +private: + // Whether a color value was explicitly set on the specific object that this attached style object represents. + bool m_explicitTheme = false; + // The actual values for this item, whether explicit, inherited or globally set. + Theme m_theme = Light; +}; + +#endif // MYSTYLE_H diff --git a/examples/quickcontrols2/attachedstyleproperties/attachedstyleproperties.cpp b/examples/quickcontrols2/attachedstyleproperties/attachedstyleproperties.cpp new file mode 100644 index 0000000000..376e53c055 --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/attachedstyleproperties.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QDir> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + app.setOrganizationName("QtProject"); + app.setOrganizationDomain("qt-project.org"); + app.setApplicationName("Attached Objects"); + + QQmlApplicationEngine engine; +#ifdef Q_OS_MACOS + engine.addImportPath(app.applicationDirPath() + "/../PlugIns"); +#endif + const QUrl url(QStringLiteral("qrc:/attachedstyleproperties.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + engine.load(url); + + return app.exec(); +} diff --git a/examples/quickcontrols2/attachedstyleproperties/attachedstyleproperties.qml b/examples/quickcontrols2/attachedstyleproperties/attachedstyleproperties.qml new file mode 100644 index 0000000000..e08e19801f --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/attachedstyleproperties.qml @@ -0,0 +1,110 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +import MyStyle + +ApplicationWindow { + width: 800 + height: 600 + title: qsTr("Attached Objects") + visible: true + + MyStyle.theme: darkModeSwitch.checked ? MyStyle.Dark : MyStyle.Light + + header: ToolBar { + MyStyle.theme: MyStyle.Dark + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 12 + + Label { + text: qsTr("This is a Label in a ToolBar") + } + + Item { + Layout.fillWidth: true + } + + Switch { + id: darkModeSwitch + text: qsTr("Dark mode") + } + } + } + + ColumnLayout { + anchors.centerIn: parent + spacing: 20 + + Label { + text: qsTr("This is a Label in an ApplicationWindow") + Layout.alignment: Qt.AlignHCenter + } + + RowLayout { + Button { + text: qsTr("Open Popup") + onClicked: popup.open() + } + + Button { + text: qsTr("Open Window") + onClicked: { + if (!childWindow.active) + childWindow.show() + else + childWindow.raise() + } + } + } + } + + Popup { + id: popup + dim: true + anchors.centerIn: parent + + ColumnLayout { + anchors.centerIn: parent + spacing: 20 + + Label { + text: qsTr("This is a Label in a Popup") + Layout.alignment: Qt.AlignHCenter + } + + Button { + text: qsTr("Close Popup") + Layout.alignment: Qt.AlignHCenter + onClicked: popup.close() + } + } + } + + ApplicationWindow { + id: childWindow + width: 600 + height: 400 + title: qsTr("Attached Objects - Child Window") + + ColumnLayout { + anchors.centerIn: parent + spacing: 20 + + Label { + text: qsTr("This is a Label in a child ApplicationWindow") + Layout.alignment: Qt.AlignHCenter + } + + Button { + text: qsTr("Close Window") + Layout.alignment: Qt.AlignHCenter + onClicked: childWindow.close() + } + } + } +} diff --git a/examples/quickcontrols2/attachedstyleproperties/doc/images/qtquickcontrols2-attachedstyleproperties.png b/examples/quickcontrols2/attachedstyleproperties/doc/images/qtquickcontrols2-attachedstyleproperties.png Binary files differnew file mode 100644 index 0000000000..2cd5f214f7 --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/doc/images/qtquickcontrols2-attachedstyleproperties.png diff --git a/examples/quickcontrols2/attachedstyleproperties/doc/src/qtquickcontrols2-attachedstyleproperties.qdoc b/examples/quickcontrols2/attachedstyleproperties/doc/src/qtquickcontrols2-attachedstyleproperties.qdoc new file mode 100644 index 0000000000..de43fc80bd --- /dev/null +++ b/examples/quickcontrols2/attachedstyleproperties/doc/src/qtquickcontrols2-attachedstyleproperties.qdoc @@ -0,0 +1,18 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example attachedstyleproperties + \keyword Qt Quick Controls - Attached Style Properties Example + \title Qt Quick Controls - Attached Style Properties Example + \ingroup qtquickcontrols2-examples + \brief Demonstrates use of QQuickAttachedPropertyPropagator. + + \image qtquickcontrols2-attachedstyleproperties.png + + The Attached Style Properties example is a simple application that shows + how to use \l QQuickAttachedPropertyPropagator to propagate attached + property values to child objects. + + \include examples-run.qdocinc +*/ diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index 5799fda87e..e6a6dbfe36 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -595,6 +595,14 @@ qDebug() << "Value of MessageBoard.expired:" << attached->expired(); \endcode +\section3 Propagating Attached Properties + +\l QQuickAttachedPropertyPropagator can be subclassed to propagate attached properties +from a parent object to its children, similar to \l {Control::}{font} and +\l {Item::}{palette} propagation. It supports propagation through +\l {Item}{items}, \l {Popup}{popups}, and \l {Window}{windows}. + + \section2 Property Modifier Types A property modifier type is a special kind of QML object type. A property diff --git a/src/quickcontrols2/CMakeLists.txt b/src/quickcontrols2/CMakeLists.txt index 65dbf409b3..24a2ca8ca2 100644 --- a/src/quickcontrols2/CMakeLists.txt +++ b/src/quickcontrols2/CMakeLists.txt @@ -27,6 +27,7 @@ qt_internal_add_qml_module(QuickControls2 NO_PLUGIN_OPTIONAL NO_GENERATE_PLUGIN_SOURCE SOURCES + qquickattachedpropertypropagator.cpp qquickattachedpropertypropagator.h qquickstyle.cpp qquickstyle.h qquickstyle_p.h qquickstyleplugin.cpp qquickstyleplugin_p.h qtquickcontrols2global.h diff --git a/src/quickcontrols2/imagine/qquickimaginestyle.cpp b/src/quickcontrols2/imagine/qquickimaginestyle.cpp index e3270cec49..377103e2f2 100644 --- a/src/quickcontrols2/imagine/qquickimaginestyle.cpp +++ b/src/quickcontrols2/imagine/qquickimaginestyle.cpp @@ -17,7 +17,7 @@ static QString ensureSlash(const QString &path) } QQuickImagineStyle::QQuickImagineStyle(QObject *parent) - : QQuickAttachedObject(parent), + : QQuickAttachedPropertyPropagator(parent), m_path(*GlobalPath()) { init(); @@ -58,7 +58,7 @@ void QQuickImagineStyle::inheritPath(const QString &path) void QQuickImagineStyle::propagatePath() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickImagineStyle *imagine = qobject_cast<QQuickImagineStyle *>(child); if (imagine) imagine->inheritPath(m_path); @@ -98,7 +98,7 @@ QUrl QQuickImagineStyle::url() const return QUrl::fromLocalFile(path); } -void QQuickImagineStyle::attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent) +void QQuickImagineStyle::attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) { Q_UNUSED(oldParent); QQuickImagineStyle *imagine = qobject_cast<QQuickImagineStyle *>(newParent); @@ -129,7 +129,7 @@ void QQuickImagineStyle::init() globalsInitialized = true; } - QQuickAttachedObject::init(); // TODO: lazy init? + QQuickAttachedPropertyPropagator::initialize(); // TODO: lazy init? } QT_END_NAMESPACE diff --git a/src/quickcontrols2/imagine/qquickimaginestyle_p.h b/src/quickcontrols2/imagine/qquickimaginestyle_p.h index 738577080f..81f17fb58c 100644 --- a/src/quickcontrols2/imagine/qquickimaginestyle_p.h +++ b/src/quickcontrols2/imagine/qquickimaginestyle_p.h @@ -17,11 +17,11 @@ #include <QtCore/qvariant.h> #include <QtQml/qqml.h> -#include <QtQuickControls2Impl/private/qquickattachedobject_p.h> +#include <QtQuickControls2/qquickattachedpropertypropagator.h> QT_BEGIN_NAMESPACE -class QQuickImagineStyle : public QQuickAttachedObject +class QQuickImagineStyle : public QQuickAttachedPropertyPropagator { Q_OBJECT Q_PROPERTY(QString path READ path WRITE setPath RESET resetPath NOTIFY pathChanged FINAL) @@ -48,7 +48,7 @@ Q_SIGNALS: void pathChanged(); protected: - void attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent) override; + void attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) override; private: void init(); diff --git a/src/quickcontrols2/ios/qquickiosstyle.cpp b/src/quickcontrols2/ios/qquickiosstyle.cpp index f773833f00..74338d370c 100644 --- a/src/quickcontrols2/ios/qquickiosstyle.cpp +++ b/src/quickcontrols2/ios/qquickiosstyle.cpp @@ -18,7 +18,7 @@ static QQuickIOSStyle::Theme qquickios_effective_theme() } QQuickIOSStyle::QQuickIOSStyle(QObject *parent) - : QQuickAttachedObject(parent) + : QQuickAttachedPropertyPropagator(parent) { init(); m_theme = qquickios_effective_theme(); @@ -41,7 +41,7 @@ void QQuickIOSStyle::init() // QSharedPointer<QSettings> settings = QQuickStylePrivate::settings(QStringLiteral("iOS")); // globalsInitialized = true; // } - QQuickAttachedObject::init(); // TODO: lazy init? + QQuickAttachedPropertyPropagator::initialize(); // TODO: lazy init? } QQuickIOSStyle::Theme QQuickIOSStyle::theme() const diff --git a/src/quickcontrols2/ios/qquickiosstyle_p.h b/src/quickcontrols2/ios/qquickiosstyle_p.h index 49e5dde0a1..b71d9b0f86 100644 --- a/src/quickcontrols2/ios/qquickiosstyle_p.h +++ b/src/quickcontrols2/ios/qquickiosstyle_p.h @@ -17,11 +17,11 @@ #include <QtCore/qvariant.h> #include <QtQml/qqml.h> -#include <QtQuickControls2Impl/private/qquickattachedobject_p.h> +#include <QtQuickControls2/qquickattachedpropertypropagator.h> QT_BEGIN_NAMESPACE -class QQuickIOSStyle : public QQuickAttachedObject +class QQuickIOSStyle : public QQuickAttachedPropertyPropagator { Q_OBJECT Q_PROPERTY(QUrl url READ url CONSTANT) diff --git a/src/quickcontrols2/material/qquickmaterialstyle.cpp b/src/quickcontrols2/material/qquickmaterialstyle.cpp index b4c782f023..9114462828 100644 --- a/src/quickcontrols2/material/qquickmaterialstyle.cpp +++ b/src/quickcontrols2/material/qquickmaterialstyle.cpp @@ -399,7 +399,7 @@ static QQuickMaterialStyle::Theme effectiveTheme(QQuickMaterialStyle::Theme them return theme; } -QQuickMaterialStyle::QQuickMaterialStyle(QObject *parent) : QQuickAttachedObject(parent), +QQuickMaterialStyle::QQuickMaterialStyle(QObject *parent) : QQuickAttachedPropertyPropagator(parent), m_customPrimary(globalPrimaryCustom), m_customAccent(globalAccentCustom), m_customForeground(globalForegroundCustom), @@ -412,7 +412,7 @@ QQuickMaterialStyle::QQuickMaterialStyle(QObject *parent) : QQuickAttachedObject m_foreground(globalForeground), m_background(globalBackground) { - QQuickAttachedObject::init(); + QQuickAttachedPropertyPropagator::initialize(); } QQuickMaterialStyle *QQuickMaterialStyle::qmlAttachedProperties(QObject *object) @@ -464,7 +464,7 @@ void QQuickMaterialStyle::inheritTheme(Theme theme) void QQuickMaterialStyle::propagateTheme() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(child); if (material) material->inheritTheme(m_theme); @@ -530,7 +530,7 @@ void QQuickMaterialStyle::inheritPrimary(uint primary, bool custom) void QQuickMaterialStyle::propagatePrimary() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(child); if (material) material->inheritPrimary(m_primary, m_customPrimary); @@ -594,7 +594,7 @@ void QQuickMaterialStyle::inheritAccent(uint accent, bool custom) void QQuickMaterialStyle::propagateAccent() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(child); if (material) material->inheritAccent(m_accent, m_customAccent); @@ -666,7 +666,7 @@ void QQuickMaterialStyle::inheritForeground(uint foreground, bool custom, bool h void QQuickMaterialStyle::propagateForeground() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(child); if (material) material->inheritForeground(m_foreground, m_customForeground, m_hasForeground); @@ -731,7 +731,7 @@ void QQuickMaterialStyle::inheritBackground(uint background, bool custom, bool h void QQuickMaterialStyle::propagateBackground() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(child); if (material) material->inheritBackground(m_background, m_customBackground, m_hasBackground); @@ -1319,7 +1319,7 @@ void QQuickMaterialStyle::initGlobals() } } -void QQuickMaterialStyle::attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent) +void QQuickMaterialStyle::attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) { Q_UNUSED(oldParent); QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(newParent); diff --git a/src/quickcontrols2/material/qquickmaterialstyle_p.h b/src/quickcontrols2/material/qquickmaterialstyle_p.h index 5c009b124b..99344a6192 100644 --- a/src/quickcontrols2/material/qquickmaterialstyle_p.h +++ b/src/quickcontrols2/material/qquickmaterialstyle_p.h @@ -16,11 +16,12 @@ // #include <QtGui/qcolor.h> -#include <QtQuickControls2Impl/private/qquickattachedobject_p.h> +#include <QtQml/qqml.h> +#include <QtQuickControls2/qquickattachedpropertypropagator.h> QT_BEGIN_NAMESPACE -class QQuickMaterialStyle : public QQuickAttachedObject +class QQuickMaterialStyle : public QQuickAttachedPropertyPropagator { Q_OBJECT Q_PROPERTY(Theme theme READ theme WRITE setTheme RESET resetTheme NOTIFY themeChanged FINAL) @@ -256,7 +257,7 @@ Q_SIGNALS: void toolTextColorChanged(); protected: - void attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent) override; + void attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) override; private: void init(); diff --git a/src/quickcontrols2/qquickattachedpropertypropagator.cpp b/src/quickcontrols2/qquickattachedpropertypropagator.cpp new file mode 100644 index 0000000000..07bcb93d7f --- /dev/null +++ b/src/quickcontrols2/qquickattachedpropertypropagator.cpp @@ -0,0 +1,380 @@ +// Copyright (C) 2022 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 "qquickattachedpropertypropagator.h" + +#include <QtCore/qpointer.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> +#include <QtQuickTemplates2/private/qquickpopup_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickAttachedPropertyPropagator + \brief The QQuickAttachedPropertyPropagator class provides a way to + propagate attached properties. + \inmodule QtQuickControls2 + \since 6.5 + + In QML, it is possible to + \l {Attached Properties and Attached Signal Handlers}{attach properties and + signal handlers} to objects. \l {Providing Attached Properties} goes into more + detail about how to expose your own C++ attached types. + + QQuickAttachedPropertyPropagator provides an API to propagate attached + properties from a parent object to its children, similar to + \l {Control::}{font} and \l {Item::}{palette} propagation. It supports + propagation through \l {Item}{items}, \l {Popup}{popups}, and + \l {Window}{windows}. + + If propagation of properties is not important, consider using a + \l {QML_SINGLETON}{C++} or + \l {Contents of a Module Definition qmldir File}{QML} singleton instead, + as it is better suited for that use case, and is more efficient in that + it only requires one QObject. + + To use QQuickAttachedPropertyPropagator: + \list + \li Derive from it + \li Call \l initialize() in the constructor + \li Define set/inherit/propagate/reset functions for each property as needed + \li Reimplement \l attachedParentChange() to handle property inheritance + \endlist + + For an example that demonstrates this in depth, see + \l {Qt Quick Controls - Attached Style Properties Example}. + + \sa {Styling Qt Quick Controls} +*/ + +static QQuickAttachedPropertyPropagator *attachedObject(const QMetaObject *type, QObject *object, bool create = false) +{ + if (!object) + return nullptr; + auto func = qmlAttachedPropertiesFunction(object, type); + return qobject_cast<QQuickAttachedPropertyPropagator *>(qmlAttachedPropertiesObject(object, func, create)); +} + +/*! + \internal + + Tries to find a QQuickAttachedPropertyPropagator whose type is \a ourAttachedType + and is attached to an ancestor of \a objectWeAreAttachedTo. + + QQuickAttachedPropertyPropagator needs to know who its parent attached object is in + order to inherit attached property values from it. This is called when an + instance of QQuickAttachedPropertyPropagator is created, and whenever + \c {objectWeAreAttachedTo}'s parent changes, for example. +*/ +static QQuickAttachedPropertyPropagator *findAttachedParent(const QMetaObject *ourAttachedType, QObject *objectWeAreAttachedTo) +{ + QQuickItem *item = qobject_cast<QQuickItem *>(objectWeAreAttachedTo); + if (item) { + // lookup parent items and popups + QQuickItem *parent = item->parentItem(); + while (parent) { + QQuickAttachedPropertyPropagator *attached = attachedObject(ourAttachedType, parent); + if (attached) + return attached; + + QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent->parent()); + if (popup) + return attachedObject(ourAttachedType, popup); + + parent = parent->parentItem(); + } + + // fallback to item's window + QQuickAttachedPropertyPropagator *attached = attachedObject(ourAttachedType, item->window()); + if (attached) + return attached; + } else { + // lookup popup's window + QQuickPopup *popup = qobject_cast<QQuickPopup *>(objectWeAreAttachedTo); + if (popup) + return attachedObject(ourAttachedType, popup->popupItem()->window()); + } + + // lookup parent window + QQuickWindow *window = qobject_cast<QQuickWindow *>(objectWeAreAttachedTo); + if (window) { + // It doesn't seem like a parent window can be anything but transient in Qt Quick. + QQuickWindow *parentWindow = qobject_cast<QQuickWindow *>(window->transientParent()); + if (parentWindow) { + QQuickAttachedPropertyPropagator *attached = attachedObject(ourAttachedType, parentWindow); + if (attached) + return attached; + } + } + + // fallback to engine (global) + if (objectWeAreAttachedTo) { + QQmlEngine *engine = qmlEngine(objectWeAreAttachedTo); + if (engine) { + QByteArray name = QByteArray("_q_") + ourAttachedType->className(); + QQuickAttachedPropertyPropagator *attached = engine->property(name).value<QQuickAttachedPropertyPropagator *>(); + if (!attached) { + attached = attachedObject(ourAttachedType, engine, true); + engine->setProperty(name, QVariant::fromValue(attached)); + } + return attached; + } + } + + return nullptr; +} + +static QList<QQuickAttachedPropertyPropagator *> findAttachedChildren(const QMetaObject *type, QObject *object) +{ + QList<QQuickAttachedPropertyPropagator *> children; + + QQuickItem *item = qobject_cast<QQuickItem *>(object); + if (!item) { + QQuickWindow *window = qobject_cast<QQuickWindow *>(object); + if (window) + item = window->contentItem(); + } + + if (!item) + return children; + + // At this point, "item" could either be an item that the attached object is + // attached to directly, or the contentItem of a window that the attached object + // is attached to. + + // Look for attached properties on items. + const auto childItems = item->childItems(); + for (QQuickItem *child : childItems) { + QQuickAttachedPropertyPropagator *attached = attachedObject(type, child); + if (attached) + children += attached; + else + children += findAttachedChildren(type, child); + } + + // Look for attached properties on windows. Windows declared in QML + // as children of a Window are QObject-parented to the contentItem (see + // QQuickWindowPrivate::data_append()). Windows declared as children + // of items are QObject-parented to those items. + const auto &windowChildren = item->children(); + for (QObject *child : windowChildren) { + QQuickWindow *childWindow = qobject_cast<QQuickWindow *>(child); + if (childWindow) { + QQuickAttachedPropertyPropagator *attached = attachedObject(type, childWindow); + if (attached) + children += attached; + } + } + + return children; +} + +static QQuickItem *findAttachedItem(QObject *parent) +{ + QQuickItem *item = qobject_cast<QQuickItem *>(parent); + if (!item) { + QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent); + if (popup) + item = popup->popupItem(); + } + return item; +} + +class QQuickAttachedPropertyPropagatorPrivate : public QObjectPrivate, public QQuickItemChangeListener +{ +public: + Q_DECLARE_PUBLIC(QQuickAttachedPropertyPropagator) + + static QQuickAttachedPropertyPropagatorPrivate *get(QQuickAttachedPropertyPropagator *attachedObject) + { + return attachedObject->d_func(); + } + + void attachTo(QObject *object); + void detachFrom(QObject *object); + void setAttachedParent(QQuickAttachedPropertyPropagator *parent); + + void itemWindowChanged(QQuickWindow *window); + void itemParentChanged(QQuickItem *item, QQuickItem *parent) override; + + QList<QQuickAttachedPropertyPropagator *> attachedChildren; + QPointer<QQuickAttachedPropertyPropagator> attachedParent; +}; + +void QQuickAttachedPropertyPropagatorPrivate::attachTo(QObject *object) +{ + QQuickItem *item = findAttachedItem(object); + if (item) { + connect(item, &QQuickItem::windowChanged, this, &QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged); + QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Parent); + } +} + +void QQuickAttachedPropertyPropagatorPrivate::detachFrom(QObject *object) +{ + QQuickItem *item = findAttachedItem(object); + if (item) { + disconnect(item, &QQuickItem::windowChanged, this, &QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged); + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Parent); + } +} + +/*! + \internal + + This function sets the attached parent of this attached object. + + Currently it is called when: + \list + \li The target item's parent changes. + \li The target item's window changes. + \li The attached object is constructed, to set the attached parent + and the attached parent of the attached object children. + \li The attached object is destructed. + \endlist + + \quotefromfile ../../examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.cpp + \skipto MyStyle::resetTheme + \printuntil } +*/ +void QQuickAttachedPropertyPropagatorPrivate::setAttachedParent(QQuickAttachedPropertyPropagator *parent) +{ + Q_Q(QQuickAttachedPropertyPropagator); + if (attachedParent == parent) + return; + + QQuickAttachedPropertyPropagator *oldParent = attachedParent; + if (attachedParent) + QQuickAttachedPropertyPropagatorPrivate::get(attachedParent)->attachedChildren.removeOne(q); + attachedParent = parent; + if (parent) + QQuickAttachedPropertyPropagatorPrivate::get(parent)->attachedChildren.append(q); + q->attachedParentChange(parent, oldParent); +} + +void QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged(QQuickWindow *window) +{ + Q_Q(QQuickAttachedPropertyPropagator); + QQuickAttachedPropertyPropagator *attachedParent = nullptr; + QQuickItem *item = qobject_cast<QQuickItem *>(q->sender()); + if (item) + attachedParent = findAttachedParent(q->metaObject(), item); + if (!attachedParent) + attachedParent = attachedObject(q->metaObject(), window); + setAttachedParent(attachedParent); +} + +void QQuickAttachedPropertyPropagatorPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent) +{ + Q_Q(QQuickAttachedPropertyPropagator); + Q_UNUSED(parent); + setAttachedParent(findAttachedParent(q->metaObject(), item)); +} + +/*! + Constructs a QQuickAttachedPropertyPropagator with the given \a parent. + + The \c parent will be used to find this object's + \l {attachedParent()}{attached parent}. + + Derived classes should call \l initialize() in their constructor. +*/ +QQuickAttachedPropertyPropagator::QQuickAttachedPropertyPropagator(QObject *parent) + : QObject(*(new QQuickAttachedPropertyPropagatorPrivate), parent) +{ + Q_D(QQuickAttachedPropertyPropagator); + d->attachTo(parent); +} + +/*! + Destroys the QQuickAttachedPropertyPropagator. +*/ +QQuickAttachedPropertyPropagator::~QQuickAttachedPropertyPropagator() +{ + Q_D(QQuickAttachedPropertyPropagator); + d->detachFrom(parent()); + d->setAttachedParent(nullptr); +} + +/*! + This function returns the attached children of this attached object. + + The attached children are used when propagating property values: + + \quotefromfile ../../examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.cpp + \skipto MyStyle::propagateTheme + \printuntil } + \printuntil } +*/ +QList<QQuickAttachedPropertyPropagator *> QQuickAttachedPropertyPropagator::attachedChildren() const +{ + Q_D(const QQuickAttachedPropertyPropagator); + return d->attachedChildren; +} + +/*! + This function returns the attached parent of this attached object. + + The attached parent is used when inheriting property values: + + \quotefromfile ../../examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.cpp + \skipto MyStyle::resetTheme + \printuntil } +*/ +QQuickAttachedPropertyPropagator *QQuickAttachedPropertyPropagator::attachedParent() const +{ + Q_D(const QQuickAttachedPropertyPropagator); + return d->attachedParent; +} + +/*! + Finds and sets the attached parent for this attached object, and then does + the same for its children. This must be called upon construction of the + attached object in order for propagation to work. + + It can be useful to read global/default values before calling this + function. For example, before calling \c initialize(), the + \l {Imagine Style}{Imagine} style checks a static "globalsInitialized" flag + to see if it should read default values from \l QSettings. The values from + that file form the basis for any attached property values that have not + been explicitly set. + + \quotefromfile ../../examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.cpp + \skipto MyStyle::MyStyle + \printuntil } +*/ +void QQuickAttachedPropertyPropagator::initialize() +{ + Q_D(QQuickAttachedPropertyPropagator); + QQuickAttachedPropertyPropagator *attachedParent = findAttachedParent(metaObject(), parent()); + if (attachedParent) + d->setAttachedParent(attachedParent); + + const QList<QQuickAttachedPropertyPropagator *> attachedChildren = findAttachedChildren(metaObject(), parent()); + for (QQuickAttachedPropertyPropagator *child : attachedChildren) + QQuickAttachedPropertyPropagatorPrivate::get(child)->setAttachedParent(this); +} + +/*! + This function is called whenever the attached parent of this + QQuickAttachedPropertyPropagator changes from \a oldParent to \a newParent. + + Subclasses should reimplement this function to inherit attached properties + from \c newParent. + + \quotefromfile ../../examples/quickcontrols2/attachedstyleproperties/MyStyle/mystyle.cpp + \skipto attachedParentChange + \printuntil } + \printuntil } +*/ +void QQuickAttachedPropertyPropagator::attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) +{ + Q_UNUSED(newParent); + Q_UNUSED(oldParent); +} + +QT_END_NAMESPACE + +#include "moc_qquickattachedpropertypropagator.cpp" diff --git a/src/quickcontrols2/qquickattachedpropertypropagator.h b/src/quickcontrols2/qquickattachedpropertypropagator.h new file mode 100644 index 0000000000..ec5ff7991a --- /dev/null +++ b/src/quickcontrols2/qquickattachedpropertypropagator.h @@ -0,0 +1,37 @@ +// Copyright (C) 2022 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 + +#ifndef QQUICKATTACHEDOBJECT_H +#define QQUICKATTACHEDOBJECT_H + +#include <QtCore/qobject.h> +#include <QtQuickControls2/qtquickcontrols2global.h> + +QT_BEGIN_NAMESPACE + +class QQuickAttachedPropertyPropagatorPrivate; + +class Q_QUICKCONTROLS2_EXPORT QQuickAttachedPropertyPropagator : public QObject +{ + Q_OBJECT + +public: + explicit QQuickAttachedPropertyPropagator(QObject *parent = nullptr); + ~QQuickAttachedPropertyPropagator(); + + QList<QQuickAttachedPropertyPropagator *> attachedChildren() const; + + QQuickAttachedPropertyPropagator *attachedParent() const; + +protected: + void initialize(); + + virtual void attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent); + +private: + Q_DECLARE_PRIVATE(QQuickAttachedPropertyPropagator) +}; + +QT_END_NAMESPACE + +#endif // QQUICKATTACHEDOBJECT_H diff --git a/src/quickcontrols2/universal/qquickuniversalstyle.cpp b/src/quickcontrols2/universal/qquickuniversalstyle.cpp index 9de295757c..cacf20a75f 100644 --- a/src/quickcontrols2/universal/qquickuniversalstyle.cpp +++ b/src/quickcontrols2/universal/qquickuniversalstyle.cpp @@ -118,11 +118,11 @@ static QRgb GlobalBackground = qquickuniversal_light_color(QQuickUniversalStyle: static bool HasGlobalForeground = false; static bool HasGlobalBackground = false; -QQuickUniversalStyle::QQuickUniversalStyle(QObject *parent) : QQuickAttachedObject(parent), +QQuickUniversalStyle::QQuickUniversalStyle(QObject *parent) : QQuickAttachedPropertyPropagator(parent), m_hasForeground(HasGlobalForeground), m_hasBackground(HasGlobalBackground), m_theme(GlobalTheme), m_accent(GlobalAccent), m_foreground(GlobalForeground), m_background(GlobalBackground) { - init(); + initialize(); } QQuickUniversalStyle *QQuickUniversalStyle::qmlAttachedProperties(QObject *object) @@ -166,7 +166,7 @@ void QQuickUniversalStyle::inheritTheme(Theme theme) void QQuickUniversalStyle::propagateTheme() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(child); if (universal) universal->inheritTheme(m_theme); @@ -216,7 +216,7 @@ void QQuickUniversalStyle::inheritAccent(QRgb accent) void QQuickUniversalStyle::propagateAccent() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(child); if (universal) universal->inheritAccent(m_accent); @@ -270,7 +270,7 @@ void QQuickUniversalStyle::inheritForeground(QRgb foreground, bool has) void QQuickUniversalStyle::propagateForeground() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(child); if (universal) universal->inheritForeground(m_foreground, m_hasForeground); @@ -325,7 +325,7 @@ void QQuickUniversalStyle::inheritBackground(QRgb background, bool has) void QQuickUniversalStyle::propagateBackground() { const auto styles = attachedChildren(); - for (QQuickAttachedObject *child : styles) { + for (QQuickAttachedPropertyPropagator *child : styles) { QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(child); if (universal) universal->inheritBackground(m_background, m_hasBackground); @@ -473,7 +473,7 @@ QColor QQuickUniversalStyle::systemColor(SystemColor role) const return QColor::fromRgba(m_theme == QQuickUniversalStyle::Dark ? qquickuniversal_dark_color(role) : qquickuniversal_light_color(role)); } -void QQuickUniversalStyle::attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent) +void QQuickUniversalStyle::attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) { Q_UNUSED(oldParent); QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(newParent); diff --git a/src/quickcontrols2/universal/qquickuniversalstyle_p.h b/src/quickcontrols2/universal/qquickuniversalstyle_p.h index 7fa6e1a58a..024fde2192 100644 --- a/src/quickcontrols2/universal/qquickuniversalstyle_p.h +++ b/src/quickcontrols2/universal/qquickuniversalstyle_p.h @@ -16,13 +16,14 @@ // #include <QtGui/qcolor.h> -#include <QtQuickControls2Impl/private/qquickattachedobject_p.h> +#include <QtQml/qqml.h> +#include <QtQuickControls2/qquickattachedpropertypropagator.h> QT_BEGIN_NAMESPACE class QQuickUniversalStylePrivate; -class QQuickUniversalStyle : public QQuickAttachedObject +class QQuickUniversalStyle : public QQuickAttachedPropertyPropagator { Q_OBJECT Q_PROPERTY(Theme theme READ theme WRITE setTheme RESET resetTheme NOTIFY themeChanged FINAL) @@ -182,7 +183,7 @@ Q_SIGNALS: void paletteChanged(); protected: - void attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent) override; + void attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) override; private: bool variantToRgba(const QVariant &var, const char *name, QRgb *rgba) const; diff --git a/src/quickcontrols2impl/CMakeLists.txt b/src/quickcontrols2impl/CMakeLists.txt index 87a5a1634c..acbc91b15e 100644 --- a/src/quickcontrols2impl/CMakeLists.txt +++ b/src/quickcontrols2impl/CMakeLists.txt @@ -15,7 +15,6 @@ qt_internal_add_qml_module(QuickControls2Impl PLUGIN_TARGET qtquickcontrols2implplugin SOURCES qquickanimatednode.cpp qquickanimatednode_p.h - qquickattachedobject.cpp qquickattachedobject_p.h qquickchecklabel.cpp qquickchecklabel_p.h qquickclippedtext.cpp qquickclippedtext_p.h qquickcolor.cpp qquickcolor_p.h diff --git a/src/quickcontrols2impl/qquickattachedobject.cpp b/src/quickcontrols2impl/qquickattachedobject.cpp deleted file mode 100644 index a6bf6fc667..0000000000 --- a/src/quickcontrols2impl/qquickattachedobject.cpp +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (C) 2017 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 "qquickattachedobject_p.h" - -#include <QtCore/qpointer.h> -#include <QtQuick/qquickwindow.h> -#include <QtQuick/private/qquickitem_p.h> -#include <QtQuick/private/qquickitemchangelistener_p.h> -#include <QtQuickTemplates2/private/qquickpopup_p.h> - -QT_BEGIN_NAMESPACE - -static QQuickAttachedObject *attachedObject(const QMetaObject *type, QObject *object, bool create = false) -{ - if (!object) - return nullptr; - auto func = qmlAttachedPropertiesFunction(object, type); - return qobject_cast<QQuickAttachedObject *>(qmlAttachedPropertiesObject(object, func, create)); -} - -static QQuickAttachedObject *findAttachedParent(const QMetaObject *type, QObject *object) -{ - QQuickItem *item = qobject_cast<QQuickItem *>(object); - if (item) { - // lookup parent items and popups - QQuickItem *parent = item->parentItem(); - while (parent) { - QQuickAttachedObject *attached = attachedObject(type, parent); - if (attached) - return attached; - - QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent->parent()); - if (popup) - return attachedObject(type, popup); - - parent = parent->parentItem(); - } - - // fallback to item's window - QQuickAttachedObject *attached = attachedObject(type, item->window()); - if (attached) - return attached; - } else { - // lookup popup's window - QQuickPopup *popup = qobject_cast<QQuickPopup *>(object); - if (popup) - return attachedObject(type, popup->popupItem()->window()); - } - - // lookup parent window - QQuickWindow *window = qobject_cast<QQuickWindow *>(object); - if (window) { - QQuickWindow *parentWindow = qobject_cast<QQuickWindow *>(window->parent()); - if (parentWindow) { - QQuickAttachedObject *attached = attachedObject(type, window); - if (attached) - return attached; - } - } - - // fallback to engine (global) - if (object) { - QQmlEngine *engine = qmlEngine(object); - if (engine) { - QByteArray name = QByteArray("_q_") + type->className(); - QQuickAttachedObject *attached = engine->property(name).value<QQuickAttachedObject *>(); - if (!attached) { - attached = attachedObject(type, engine, true); - engine->setProperty(name, QVariant::fromValue(attached)); - } - return attached; - } - } - - return nullptr; -} - -static QList<QQuickAttachedObject *> findAttachedChildren(const QMetaObject *type, QObject *object) -{ - QList<QQuickAttachedObject *> children; - - QQuickItem *item = qobject_cast<QQuickItem *>(object); - if (!item) { - QQuickWindow *window = qobject_cast<QQuickWindow *>(object); - if (window) { - item = window->contentItem(); - - const auto &windowChildren = window->children(); - for (QObject *child : windowChildren) { - QQuickWindow *childWindow = qobject_cast<QQuickWindow *>(child); - if (childWindow) { - QQuickAttachedObject *attached = attachedObject(type, childWindow); - if (attached) - children += attached; - } - } - } - } - - if (item) { - const auto childItems = item->childItems(); - for (QQuickItem *child : childItems) { - QQuickAttachedObject *attached = attachedObject(type, child); - if (attached) - children += attached; - else - children += findAttachedChildren(type, child); - } - } - - return children; -} - -static QQuickItem *findAttachedItem(QObject *parent) -{ - QQuickItem *item = qobject_cast<QQuickItem *>(parent); - if (!item) { - QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent); - if (popup) - item = popup->popupItem(); - } - return item; -} - -class QQuickAttachedObjectPrivate : public QObjectPrivate, public QQuickItemChangeListener -{ -public: - Q_DECLARE_PUBLIC(QQuickAttachedObject) - - static QQuickAttachedObjectPrivate *get(QQuickAttachedObject *attachedObject) - { - return attachedObject->d_func(); - } - - void attachTo(QObject *object); - void detachFrom(QObject *object); - - void itemWindowChanged(QQuickWindow *window); - void itemParentChanged(QQuickItem *item, QQuickItem *parent) override; - - QList<QQuickAttachedObject *> attachedChildren; - QPointer<QQuickAttachedObject> attachedParent; -}; - -void QQuickAttachedObjectPrivate::attachTo(QObject *object) -{ - QQuickItem *item = findAttachedItem(object); - if (item) { - connect(item, &QQuickItem::windowChanged, this, &QQuickAttachedObjectPrivate::itemWindowChanged); - QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Parent); - } -} - -void QQuickAttachedObjectPrivate::detachFrom(QObject *object) -{ - QQuickItem *item = findAttachedItem(object); - if (item) { - disconnect(item, &QQuickItem::windowChanged, this, &QQuickAttachedObjectPrivate::itemWindowChanged); - QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Parent); - } -} - -void QQuickAttachedObjectPrivate::itemWindowChanged(QQuickWindow *window) -{ - Q_Q(QQuickAttachedObject); - QQuickAttachedObject *attachedParent = nullptr; - QQuickItem *item = qobject_cast<QQuickItem *>(q->sender()); - if (item) - attachedParent = findAttachedParent(q->metaObject(), item); - if (!attachedParent) - attachedParent = attachedObject(q->metaObject(), window); - q->setAttachedParent(attachedParent); -} - -void QQuickAttachedObjectPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent) -{ - Q_Q(QQuickAttachedObject); - Q_UNUSED(parent); - q->setAttachedParent(findAttachedParent(q->metaObject(), item)); -} - -QQuickAttachedObject::QQuickAttachedObject(QObject *parent) - : QObject(*(new QQuickAttachedObjectPrivate), parent) -{ - Q_D(QQuickAttachedObject); - d->attachTo(parent); -} - -QQuickAttachedObject::~QQuickAttachedObject() -{ - Q_D(QQuickAttachedObject); - d->detachFrom(parent()); - setAttachedParent(nullptr); -} - -QList<QQuickAttachedObject *> QQuickAttachedObject::attachedChildren() const -{ - Q_D(const QQuickAttachedObject); - return d->attachedChildren; -} - -QQuickAttachedObject *QQuickAttachedObject::attachedParent() const -{ - Q_D(const QQuickAttachedObject); - return d->attachedParent; -} - -void QQuickAttachedObject::setAttachedParent(QQuickAttachedObject *parent) -{ - Q_D(QQuickAttachedObject); - if (d->attachedParent == parent) - return; - - QQuickAttachedObject *oldParent = d->attachedParent; - if (d->attachedParent) - QQuickAttachedObjectPrivate::get(d->attachedParent)->attachedChildren.removeOne(this); - d->attachedParent = parent; - if (parent) - QQuickAttachedObjectPrivate::get(parent)->attachedChildren.append(this); - attachedParentChange(parent, oldParent); -} - -void QQuickAttachedObject::init() -{ - QQuickAttachedObject *attachedParent = findAttachedParent(metaObject(), parent()); - if (attachedParent) - setAttachedParent(attachedParent); - - const QList<QQuickAttachedObject *> attachedChildren = findAttachedChildren(metaObject(), parent()); - for (QQuickAttachedObject *child : attachedChildren) - child->setAttachedParent(this); -} - -void QQuickAttachedObject::attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent) -{ - Q_UNUSED(newParent); - Q_UNUSED(oldParent); -} - -QT_END_NAMESPACE - -#include "moc_qquickattachedobject_p.cpp" diff --git a/src/quickcontrols2impl/qquickattachedobject_p.h b/src/quickcontrols2impl/qquickattachedobject_p.h deleted file mode 100644 index 3473c16936..0000000000 --- a/src/quickcontrols2impl/qquickattachedobject_p.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2017 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 - -#ifndef QQUICKATTACHEDOBJECT_P_H -#define QQUICKATTACHEDOBJECT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtQml/qqml.h> -#include <QtCore/qobject.h> -#include <QtQuickControls2Impl/private/qtquickcontrols2implglobal_p.h> - -QT_BEGIN_NAMESPACE - -class QQuickAttachedObjectPrivate; - -class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickAttachedObject : public QObject -{ - Q_OBJECT - -public: - explicit QQuickAttachedObject(QObject *parent = nullptr); - ~QQuickAttachedObject(); - - QList<QQuickAttachedObject *> attachedChildren() const; - - QQuickAttachedObject *attachedParent() const; - void setAttachedParent(QQuickAttachedObject *parent); - -protected: - void init(); - - virtual void attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent); - -private: - Q_DISABLE_COPY(QQuickAttachedObject) - Q_DECLARE_PRIVATE(QQuickAttachedObject) -}; - -QT_END_NAMESPACE - -#endif // QQUICKATTACHEDOBJECT_P_H diff --git a/tests/auto/quickcontrols2/qquickmaterialstyle/data/tst_material.qml b/tests/auto/quickcontrols2/qquickmaterialstyle/data/tst_material.qml index 689d90ca25..34754420d0 100644 --- a/tests/auto/quickcontrols2/qquickmaterialstyle/data/tst_material.qml +++ b/tests/auto/quickcontrols2/qquickmaterialstyle/data/tst_material.qml @@ -275,49 +275,199 @@ TestCase { compare(popupObject.label2.color.toString(), popupObject.Material.textSelectionColor.toString()) } - function test_window() { - let parent = createTemporaryObject(windowComponent) - - let control = button.createObject(parent.contentItem) - compare(control.Material.primary, parent.Material.primary) - compare(control.Material.accent, parent.Material.accent) - compare(control.Material.background, parent.Material.background) - compare(control.Material.foreground, parent.Material.foreground) - compare(control.Material.theme, parent.Material.theme) - - let styledChild = styledWindowComponent.createObject(parent) - verify(styledChild.Material.primary !== parent.Material.primary) - verify(styledChild.Material.accent !== parent.Material.accent) - verify(styledChild.Material.background !== parent.Material.background) - verify(styledChild.Material.foreground !== parent.Material.foreground) - verify(styledChild.Material.theme !== parent.Material.theme) - - let unstyledChild = windowComponent.createObject(parent) - compare(unstyledChild.Material.primary, parent.Material.primary) - compare(unstyledChild.Material.accent, parent.Material.accent) - compare(unstyledChild.Material.background, parent.Material.background) - compare(unstyledChild.Material.foreground, parent.Material.foreground) - compare(unstyledChild.Material.theme, parent.Material.theme) - - parent.Material.primary = Material.Lime + component StyledChildWindow: Window { + objectName: "styledChildWindow" + + Material.objectName: objectName + "MaterialAttached" + Material.theme: Material.Dark + Material.primary: Material.Brown + Material.accent: Material.Green + Material.background: Material.Yellow + Material.foreground: Material.Grey + } + + component StyledChildAppWindow: ApplicationWindow { + objectName: "styledChildAppWindow" + + Material.objectName: objectName + "MaterialAttached" + Material.theme: Material.Dark + Material.primary: Material.Brown + Material.accent: Material.Green + Material.background: Material.Yellow + Material.foreground: Material.Grey + } + + component UnstyledChildWindow: Window { + objectName: "unstyledChildWindow" + Material.objectName: objectName + "MaterialAttached" + } + + component UnstyledChildAppWindow: ApplicationWindow { + objectName: "unstyledChildAppWindow" + Material.objectName: objectName + "MaterialAttached" + } + + Component { + id: parentWindowComponent + + Window { + objectName: "rootWindow" + + Material.objectName: objectName + "MaterialAttached" + + property alias styledChildWindow: styledChildWindow + property alias unstyledChildWindow: unstyledChildWindow + + StyledChildWindow { + id: styledChildWindow + } + + UnstyledChildWindow { + id: unstyledChildWindow + } + } + } + + Component { + id: parentAppWindowComponent + + ApplicationWindow { + objectName: "rootAppWindow" + + Material.objectName: objectName + "MaterialAttached" + + property alias styledChildWindow: styledChildWindow + property alias unstyledChildWindow: unstyledChildWindow + + StyledChildAppWindow { + id: styledChildWindow + } + + UnstyledChildAppWindow { + id: unstyledChildWindow + } + } + } + + Component { + id: parentMixed1WindowComponent + + ApplicationWindow { + objectName: "rootAppWindow" + + Material.objectName: objectName + "MaterialAttached" + + property alias styledChildWindow: styledChildWindow + property alias unstyledChildWindow: unstyledChildWindow + + StyledChildWindow { + id: styledChildWindow + } + + UnstyledChildWindow { + id: unstyledChildWindow + } + } + } + + Component { + id: parentMixed2WindowComponent + + Window { + id: rootWindow + objectName: "rootWindow" + + Material.objectName: objectName + "MaterialAttached" + + property alias styledChildWindow: styledChildWindow + property alias unstyledChildWindow: unstyledChildWindow + + StyledChildAppWindow { + id: styledChildWindow + } + + UnstyledChildAppWindow { + id: unstyledChildWindow + } + } + } + + function test_window_data() { + return [ + { tag: "Window", component: parentWindowComponent }, + { tag: "ApplicationWindow", component: parentAppWindowComponent }, + // Test a combination of Window and ApplicationWindow. + { tag: "mixed-1", component: parentMixed1WindowComponent }, + { tag: "mixed-2", component: parentMixed2WindowComponent }, + ] + } + + function test_window(data) { + let parentWindow = createTemporaryObject(data.component, null) + verify(parentWindow) + + let control = button.createObject(parentWindow.contentItem) + verify(control) + compare(control.Material.primary, parentWindow.Material.primary) + compare(control.Material.accent, parentWindow.Material.accent) + compare(control.Material.background, parentWindow.Material.background) + compare(control.Material.foreground, parentWindow.Material.foreground) + compare(control.Material.theme, parentWindow.Material.theme) + + let styledChildWindow = parentWindow.styledChildWindow + verify(styledChildWindow) + verify(styledChildWindow.Material.primary !== parentWindow.Material.primary) + verify(styledChildWindow.Material.accent !== parentWindow.Material.accent) + verify(styledChildWindow.Material.background !== parentWindow.Material.background) + verify(styledChildWindow.Material.foreground !== parentWindow.Material.foreground) + verify(styledChildWindow.Material.theme !== parentWindow.Material.theme) + + let unstyledChildWindow = parentWindow.unstyledChildWindow + verify(unstyledChildWindow) + compare(unstyledChildWindow.Material.primary, parentWindow.Material.primary) + compare(unstyledChildWindow.Material.accent, parentWindow.Material.accent) + compare(unstyledChildWindow.Material.background, parentWindow.Material.background) + compare(unstyledChildWindow.Material.foreground, parentWindow.Material.foreground) + compare(unstyledChildWindow.Material.theme, parentWindow.Material.theme) + + parentWindow.Material.primary = Material.Lime compare(control.Material.primary, Material.color(Material.Lime)) - verify(styledChild.Material.primary !== Material.color(Material.Lime)) - // ### TODO: compare(unstyledChild.Material.primary, Material.color(Material.Lime)) + verify(styledChildWindow.Material.primary !== Material.color(Material.Lime)) + compare(unstyledChildWindow.Material.primary, Material.color(Material.Lime)) - parent.Material.accent = Material.Cyan + parentWindow.Material.accent = Material.Cyan compare(control.Material.accent, Material.color(Material.Cyan)) - verify(styledChild.Material.accent !== Material.color(Material.Cyan)) - // ### TODO: compare(unstyledChild.Material.accent, Material.color(Material.Cyan)) + verify(styledChildWindow.Material.accent !== Material.color(Material.Cyan)) + compare(unstyledChildWindow.Material.accent, Material.color(Material.Cyan)) - parent.Material.background = Material.Indigo + parentWindow.Material.background = Material.Indigo compare(control.Material.background, Material.color(Material.Indigo)) - verify(styledChild.Material.background !== Material.color(Material.Indigo)) - // ### TODO: compare(unstyledChild.Material.background, Material.color(Material.Indigo)) + verify(styledChildWindow.Material.background !== Material.color(Material.Indigo)) + compare(unstyledChildWindow.Material.background, Material.color(Material.Indigo)) - parent.Material.foreground = Material.Pink + parentWindow.Material.foreground = Material.Pink compare(control.Material.foreground, Material.color(Material.Pink)) - verify(styledChild.Material.foreground !== Material.color(Material.Pink)) - // ### TODO: compare(unstyledChild.Material.foreground, Material.color(Material.Pink)) + verify(styledChildWindow.Material.foreground !== Material.color(Material.Pink)) + compare(unstyledChildWindow.Material.foreground, Material.color(Material.Pink)) + + // Test that theme changes are propagated to child windows. + // Make sure that this check actually does something (in case the default changes). + compare(parentWindow.Material.theme, Material.Light) + // styledChildWindow was already dark, so make it light to verify that it doesn't change. + styledChildWindow.Material.theme = Material.Light + // Reset background since Theme affects it and we use it to test that the colors change. + parentWindow.Material.background = undefined + // Setting theme to Dark should result in the background colors of control and unstyledChildWindow changing. + // Note that since Window is unstyled, its actual (background) color won't change. + parentWindow.Material.theme = Material.Dark + compare(control.Material.theme, Material.Dark) + compare(styledChildWindow.Material.theme, Material.Light) + compare(unstyledChildWindow.Material.theme, Material.Dark) + // Make sure that the colors actually changed. + compare(parentWindow.Material.background, Qt.color("#303030")) + compare(control.Material.background, parentWindow.Material.background) + verify(styledChildWindow.Material.background !== parentWindow.Material.background) + compare(unstyledChildWindow.Material.background, parentWindow.Material.background) } function test_loader() { |