c++ 如何在Qml中将附加属性传播到子对象?

egmofgnx  于 2023-02-01  发布在  其他
关注(0)|答案(2)|浏览(137)

我想做一个类似于Material.accent的东西,在那里我可以改变父属性和子属性,得到父属性的定义。
这是我当时做的方法,但是我在文档中找不到任何关于它的信息。我知道这是可能的,材质样式也使用这个方法和其他东西,比如字体属性。

class MyThemeAttached : public QObject {
    Q_OBJECT

    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
    QML_ANONYMOUS

public:
    explicit MyThemeAttached(QObject* parent = nullptr)
        : QObject(parent)
    , m_color("#FF0000"){}

    QColor color() const;
    void setColor(const QColor color);

signals:
    void backgroundChanged(QColor background);

private:
    QColor m_color;
};

class MyTheme : public QObject
{
    Q_OBJECT
    QML_ATTACHED(MyThemeAttached)
    QML_ELEMENT
public:
    explicit MyTheme(QObject *parent = nullptr);

    static MyThemeAttached *qmlAttachedProperties(QObject *object) {
        return new MyThemeAttached(object);
    }
};

ApplicationWindow {
    id: root
    visible: true
    width: 800
    height: 600
    title: qsTr("Window")
    
    MyCustomProperty.color: "orange"

    Rectangle {
        color: MyCustomProperty.color        
    }
}
vptzau2j

vptzau2j1#

为什么不看看Material的代码呢?我会向你介绍Woboq.org。
Here您可以看到,Material主题实际上是主动地将主题推向儿童:

void QQuickMaterialStyle::propagateTheme()
{
    const auto styles = attachedChildren();
    for (QQuickAttachedObject *child : styles) {
        QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(child);
        if (material)
            material->inheritTheme(m_theme);
    }
}
pqwbnv8z

pqwbnv8z2#

在Qt 6.5中,您可以使用QQuickAttachedPropertyPropagator来实现这一点。
要使用快速附加属性传播器:

  • 从中推导
  • 在构造函数中调用initialize()
  • 根据需要为每个属性定义设置/继承/传播/重置函数
  • 重新实现attachedParentChange()以处理属性继承

下面是示例中的相关文件,以供参考。
mystyle.h

// 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

mystyle.cpp

// 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
{
    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...
    }
}

相关问题