aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlpropertytopropertybinding.cpp
blob: ec77e8c97a91c21a94e19c54b57c56a86642d714 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// 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 "qqmlpropertytopropertybinding_p.h"
#include <private/qqmlvmemetaobject_p.h>

QT_BEGIN_NAMESPACE

/*!
 * \internal
 * \class QQmlPropertyToPropertyBinding
 *
 * This class can be used to create a direct binding from a source property to
 * a target property, without going through QQmlJavaScriptExpression and
 * QV4::Function. In particular you don't need a compilation unit or byte code
 * to set this up.
 */

QQmlPropertyToPropertyBinding::QQmlPropertyToPropertyBinding(
        QQmlEngine *engine, QObject *sourceObject, int sourcePropertyIndex,
        QObject *targetObject, int targetPropertyIndex)
    : QQmlNotifierEndpoint(QQmlPropertyGuard)
    , m_engine(engine)
    , m_sourceObject(sourceObject)
    , m_sourcePropertyIndex(sourcePropertyIndex)
{
    setTarget(targetObject, targetPropertyIndex, false, -1);
}

QQmlAbstractBinding::Kind QQmlPropertyToPropertyBinding::kind() const
{
    return PropertyToPropertyBinding;
}

void QQmlPropertyToPropertyBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
{
    const bool wasEnabled = enabledFlag();
    setEnabledFlag(e);
    updateCanUseAccessor();
    if (e && !wasEnabled)
        update(flags);
}

void QQmlPropertyToPropertyBinding::captureProperty(
        const QMetaObject *sourceMetaObject, int notifyIndex,
        bool isSourceBindable, bool isTargetBindable)
{
    if (isSourceBindable) {
        // if the property is a QPropery, and we're binding to a QProperty
        // the automatic capturing process already takes care of everything
        if (isTargetBindable)
            return;

        // We have already captured.
        if (observer)
            return;

        observer = std::make_unique<Observer>(this);
        QUntypedBindable bindable;
        void *argv[] = { &bindable };
        sourceMetaObject->metacall(
                    m_sourceObject, QMetaObject::BindableProperty, m_sourcePropertyIndex, argv);
        bindable.observe(observer.get());
        return;
    }

    // We cannot capture non-bindable properties without signals
    if (notifyIndex == -1)
        return;

    if (isConnected(m_sourceObject, notifyIndex))
        cancelNotify();
    else
        connect(m_sourceObject, notifyIndex, m_engine, true);
}

void QQmlPropertyToPropertyBinding::update(QQmlPropertyData::WriteFlags flags)
{
    if (!enabledFlag())
        return;

    // Check that the target has not been deleted
    QObject *target = targetObject();
    if (QQmlData::wasDeleted(target))
        return;

    const QQmlPropertyData *d = nullptr;
    QQmlPropertyData vtd;
    getPropertyData(&d, &vtd);
    Q_ASSERT(d);

    // Check for a binding update loop
    if (Q_UNLIKELY(updatingFlag())) {
        QQmlAbstractBinding::printBindingLoopError(
                    QQmlPropertyPrivate::restore(target, *d, &vtd, nullptr));
        return;
    }

    setUpdatingFlag(true);

    if (canUseAccessor())
        flags.setFlag(QQmlPropertyData::BypassInterceptor);

    const QMetaObject *sourceMetaObject = m_sourceObject->metaObject();
    const QMetaProperty property = sourceMetaObject->property(m_sourcePropertyIndex);
    if (!property.isConstant()) {
        captureProperty(sourceMetaObject, QMetaObjectPrivate::signalIndex(property.notifySignal()),
                        property.isBindable(), !vtd.isValid() && d->isBindable());
    }

    QQmlPropertyPrivate::writeValueProperty(
            target, *d, vtd, property.read(m_sourceObject), {}, flags);

    setUpdatingFlag(false);
}

void QQmlPropertyGuard_callback(QQmlNotifierEndpoint *e, void **)
{
    static_cast<QQmlPropertyToPropertyBinding *>(e)->update();
}

void QQmlPropertyToPropertyBinding::Observer::trigger(
        QPropertyObserver *observer, QUntypedPropertyData *)
{
    static_cast<Observer *>(observer)->binding->update();
}

QT_END_NAMESPACE