Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlsynchronizer.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <private/qobject_p.h>
8#include <private/qqmlproperty_p.h>
9#include <private/qqmlvaluetype_p.h>
10#include <private/qv4scopedvalue_p.h>
11
12#include <QtQml/qqmlinfo.h>
13#include <QtCore/qcompare.h>
14
16
18{
21 QObject *object, const QQmlPropertyData *core,
22 const QQmlPropertyData *valueTypeData = nullptr)
24 , m_core(core)
26 {}
27
28 bool isValid() const { return m_object != nullptr && m_core != nullptr; }
29
30 QVariant read() const;
31 void write(QVariant &&value) const;
32
33 QObject *object() const { return m_object; }
34 const QQmlPropertyData *core() const { return m_core; }
35 const QQmlPropertyData *valueTypeData() const { return m_valueTypeData; }
36
37 QString name() const
38 {
39 if (!m_core)
40 return QString();
41
42 const QString coreName = m_core->name(m_object);
43
44 const QQmlPropertyData *vt = valueTypeData();
45 if (!vt)
46 return coreName;
47
48 const QMetaObject *vtMetaObject = QQmlMetaType::metaObjectForValueType(m_core->propType());
49 Q_ASSERT(vtMetaObject);
50 const char *vtName = vtMetaObject->property(vt->coreIndex()).name();
51 return coreName + QLatin1Char('.') + QString::fromUtf8(vtName);
52
53 }
54
55 int notifyIndex() const { return m_core->notifyIndex(); }
56
57 void clear()
58 {
59 m_object = nullptr;
60 m_core = nullptr;
61 m_valueTypeData = nullptr;
62 }
63
64 QVariant coerce(const QVariant &source, QQmlSynchronizer *q) const;
65
66private:
67 QPointer<QObject> m_object;
68 const QQmlPropertyData *m_core = nullptr;
69 const QQmlPropertyData *m_valueTypeData = nullptr;
70
71 friend bool comparesEqual(
72 const QQmlSynchronizerProperty &a, const QQmlSynchronizerProperty &b) noexcept
73 {
74 return a.m_core == b.m_core && a.m_valueTypeData == b.m_valueTypeData
75 && a.m_object == b.m_object;
76 }
78
80 {
81 // We'd better not use m_object here since it may turn to nullptr spontaneously.
82 // This results in a weaker hash, but we can live with it.
84 }
85
86
87 QMetaType metaType() const
88 {
89 return m_valueTypeData
91 : m_core->propType();
92 }
93};
94
96{
97public:
99 QQmlSynchronizer *receiver, const QQmlSynchronizerProperty &property)
103 {
104 }
105
107 {
108 QObject::disconnect(m_connection);
109 }
110
111 bool contains(const QQmlSynchronizerProperty &p) const { return m_property == p; }
112 void write(QVariant value) const { m_property.write(std::move(value)); }
113 QVariant coerce(const QVariant &source, QQmlSynchronizer *q) const
114 {
115 return m_property.coerce(source, q);
116 }
117 QQmlSynchronizerProperty property() const { return m_property; }
118
119 static void impl(int which, QtPrivate::QSlotObjectBase *self, QObject *r, void **a, bool *ret);
120 void operator()(QQmlSynchronizer *receiver)
121 {
122 impl(QtPrivate::QSlotObjectBase::Call, this, receiver, nullptr, nullptr);
123 }
124
125 void addref() { ref(); }
126 void release() { destroyIfLastRef(); }
127
128private:
129 QMetaObject::Connection createConnection(QQmlSynchronizer *receiver)
130 {
131 Q_ASSERT(receiver);
132 QObject *object = m_property.object();
133 Q_ASSERT(object);
134 const int notifyIndex = m_property.notifyIndex();
135 Q_ASSERT(notifyIndex != -1);
136
137 return QObjectPrivate::connectImpl(
138 object, notifyIndex, receiver, nullptr, this, Qt::AutoConnection, nullptr,
139 object->metaObject());
140 }
141
142
143 QQmlSynchronizerProperty m_property;
144 QMetaObject::Connection m_connection;
145};
146
148{
149public:
151 QQmlSynchronizerPrivate *receiver, const QQmlSynchronizerProperty &property)
153 , m_synchronizer(receiver)
154 {}
155
156 bool contains(const QQmlSynchronizerProperty &p) const { return m_property == p; }
157 void write(QVariant value) const { m_property.write(std::move(value)); }
158 QVariant coerce(const QVariant &source, QQmlSynchronizer *q) const
159 {
160 return m_property.coerce(source, q);
161 }
162 QQmlSynchronizerProperty property() const { return m_property; }
163
164 void operator()() const;
165
166protected:
168 {
169 const QQmlPropertyData *core = m_property.core();
170 Q_ASSERT(core && core->isValid());
171 QObject *object = m_property.object();
172 Q_ASSERT(object);
173
174 QUntypedBindable bindable;
175 void *argv[1] { &bindable };
176 core->doMetacall<QMetaObject::BindableProperty>(object, core->coreIndex(), argv);
177 Q_ASSERT(bindable.isValid());
178
179 return bindable.onValueChanged(*this);
180 }
181
182private:
183 QQmlSynchronizerProperty m_property;
184 QQmlSynchronizerPrivate *m_synchronizer = nullptr;
185};
186
188{
189public:
195
196private:
197 QPropertyChangeHandler<QQmlSynchronizerHandler> changeHandler;
198};
199
201{
202 Q_DECLARE_PUBLIC(QQmlSynchronizer)
203public:
211
217
224
225 static QQmlSynchronizerPrivate *get(QQmlSynchronizer *q) { return q->d_func(); }
226
229
230 // Target set by QQmlPropertyValueSource
231 // This can't change, but we have to hold on to the QQmlPropertyData.
233
234 // Target set using sourceObject/sourceProperty properties
235 // The string is the primary source of truth for sourceProperty.
236 // The QQmlPropertyData are only used if the object does not have a property cache.
239
240 // Target set using targetObject/targetProperty properties
241 // The string is the primary source of truth for targetProperty.
242 // The QQmlPropertyData are only used if the object does not have a property cache.
245
246 State *currentState = nullptr;
247
249
250 void createConnection(QQmlSynchronizer *q, const QQmlSynchronizerProperty &property);
251
252 void disconnectObjectProperty(const QString &property, OwnedTarget *objectProperty);
254 const QString &property, OwnedTarget *objectProperty, QQmlSynchronizer *q);
255
257
258 void initialize(QQmlSynchronizer *q);
259
261};
262
263/*!
264 \qmlmodule Qt.labs.synchronizer
265 \title Qt Labs Synchronizer QML Types
266 \ingroup qmlmodules
267 \brief Synchronizer synchronizes values between two or more properties.
268
269 To use this module, import the module with the following line:
270
271 \qml
272 import Qt.labs.synchronizer
273 \endqml
274*/
275
276/*!
277 \qmltype Synchronizer
278 \inqmlmodule Qt.labs.synchronizer
279 \since 6.10
280 \brief Synchronizes values between two or more properties.
281
282 A Synchronizer object binds two or more properties together so that a change
283 to any one of them automatically updates all others. While doing so, none of
284 the bindings on any of the properties are broken. You can use Synchronizer if
285 the direction of data flow between two properties is not pre-determined. For
286 example, a TextInput may be initialized with a model value but should also
287 update the model value when editing is finished.
288
289 \note The input elements provided by Qt Quick and Qt Quick Controls solve this
290 problem by providing user interaction signals separate from value change
291 signals and hiding the value assignments in C++ code. You \e{don't} need
292 Synchronizer for their internals. However, it may still be useful when
293 connecting a control to a model.
294
295 Consider the following example.
296
297 \section1 Without Synchronizer
298
299 \qml
300 // MyCustomTextInput.qml
301 Item {
302 property string text
303 function append(characters: string) { text += characters }
304 [...]
305 }
306 \endqml
307
308 You may be inclined to populate the \c text property from a model and update
309 the model when the \c textChanged signal is received.
310
311 \qml
312 // Does not work!
313 Item {
314 id: root
315 property string model: "lorem ipsum"
316 MyCustomTextInput {
317 text: root.model
318 onTextChanged: root.model = text
319 }
320 }
321 \endqml
322
323 This does \e not work. When the \c append function is called, the
324 \c text property is modified, and the binding that updates it from the
325 \c model property is broken. The next time the \c model is updated
326 independently \c text is not updated anymore.
327
328 To solve this, you can omit the binding altogether and use only signals
329 for updating both properties. This way you would need to give up the
330 convenience of bindings.
331
332 Or, you can use Synchronizer.
333
334 \section1 With Synchronizer
335
336 \qml
337 Item {
338 id: root
339 property string model: "lorem ipsum"
340 MyCustomTextInput {
341 Synchronizer on text {
342 property alias source: root.model
343 }
344 }
345 }
346 \endqml
347
348 Synchronizer makes sure that whenever either the model or
349 the text change, the other one is updated.
350
351 You can specify properties to be synchronized in several ways:
352 \list
353 \li Using the \c on syntax
354 \li Populating the \c sourceObject and \c sourceProperty properties
355 \li Populating the \c targetObject and \c targetProperty properties
356 \li Creating aliases in the scope of the synchronizer
357 \endlist
358
359 The following example synchronizes four different properties,
360 exercising all the different options:
361
362 \qml
363 Item {
364 id: root
365 property string model: "lorem ipsum"
366
367 MyCustomTextInput {
368 Synchronizer on text {
369 sourceObject: other
370 sourceProperty: "text"
371
372 targetObject: root.children[0]
373 targetProperty: "objectName"
374
375 property alias source: root.model
376 property alias another: root.objectName
377 }
378 }
379
380 MyCustomTextInput {
381 id: other
382 }
383 }
384 \endqml
385
386 Optionally, Synchronizer will perform an initial synchronization:
387 \list
388 \li If one of the aliases is called \c{source}, then it will be used to
389 initialize the other properties.
390 \li Otherwise, if the values assigned to \l{sourceObject} and
391 \l{sourceProperty} denote a property, that property will be used
392 as source for initial synchronization.
393 \li Otherwise, if the \c on syntax is used, the property on which the
394 Synchronizer is created that way is used as source for initial
395 synchronization.
396 \li Otherwise no initial synchronization is performed. Only when one of
397 the properties changes the others will be updated.
398 \endlist
399
400 Synchronizer automatically \e{de-bounces}. While it is synchronizing
401 using a given value as the source, it does not accept further updates from
402 one of the properties expected to be the target of the update. Such
403 behavior would otherwise easily lead to infinite update loops.
404 Synchronizer uses the \l{valueBounced} signal to notify about this
405 condition. Furthermore, it detects properties that silently
406 refuse an update and emits the \l{valueIgnored} signal for them.
407 Silence, in this context, is determined by the lack of a change signal
408 after calling the setter for the given property.
409
410 If the properties to be synchronized are of different types, the usual
411 QML type coercions are applied.
412
413 \note It is not possible to create an alias to a property of a singleton.
414 When using Synchronizer together with singletons, use \l{sourceObject} and
415 \l{sourceProperty} and the respective target properties.
416 */
417
418QQmlSynchronizer::QQmlSynchronizer(QObject *parent)
419 : QObject(*(new QQmlSynchronizerPrivate), parent)
420{
421}
422
423void QQmlSynchronizer::setTarget(const QQmlProperty &target)
424{
425 Q_D(QQmlSynchronizer);
426
427 // Should be set before component completion
428 Q_ASSERT(!d->isComponentFinalized);
429
430 QQmlPropertyPrivate *p = QQmlPropertyPrivate::get(target);
431 d->target.object = p->object;
432 d->target.core = std::make_unique<QQmlPropertyData>(p->core);
433 d->target.auxiliary = p->valueTypeData.isValid()
434 ? std::make_unique<QQmlPropertyData>(p->valueTypeData)
435 : nullptr;
436}
437
438void QQmlSynchronizer::componentFinalized()
439{
440 Q_D(QQmlSynchronizer);
441
442 d->isComponentFinalized = true;
443 d->initialize(this);
444}
445
446/*!
447 \qmlproperty QtObject Qt.labs.synchronizer::Synchronizer::sourceObject
448
449 This property holds the \l{sourceObject} part of the
450 \l{sourceObject}/\l{sourceProperty} pair that together can specify one of
451 the properties Synchronizer will synchronize.
452*/
453QObject *QQmlSynchronizer::sourceObject() const
454{
455 Q_D(const QQmlSynchronizer);
456 return d->sourceObjectProperty.object;
457}
458
459void QQmlSynchronizer::setSourceObject(QObject *object)
460{
461 Q_D(QQmlSynchronizer);
462 if (object == d->sourceObjectProperty.object)
463 return;
464
465 if (d->isComponentFinalized)
466 d->disconnectObjectProperty(d->sourceProperty, &d->sourceObjectProperty);
467
468 d->sourceObjectProperty.object = object;
469 emit sourceObjectChanged();
470
471 if (d->isComponentFinalized)
472 d->connectObjectProperty(d->sourceProperty, &d->sourceObjectProperty, this);
473}
474
475/*!
476 \qmlproperty string Qt.labs.synchronizer::Synchronizer::sourceProperty
477
478 This sourceProperty holds the \l{sourceProperty} part of the
479 \l{sourceObject}/\l{sourceProperty} pair that together can specify one of
480 the properties Synchronizer will synchronize.
481*/
482QString QQmlSynchronizer::sourceProperty() const
483{
484 Q_D(const QQmlSynchronizer);
485 return d->sourceProperty;
486}
487
488void QQmlSynchronizer::setSourceProperty(const QString &property)
489{
490 Q_D(QQmlSynchronizer);
491 if (property == d->sourceProperty)
492 return;
493
494 if (d->isComponentFinalized)
495 d->disconnectObjectProperty(d->sourceProperty, &d->sourceObjectProperty);
496
497 d->sourceProperty = property;
498 emit sourcePropertyChanged();
499
500 if (d->isComponentFinalized)
501 d->connectObjectProperty(d->sourceProperty, &d->sourceObjectProperty, this);
502}
503
504/*!
505 \qmlproperty QtObject Qt.labs.synchronizer::Synchronizer::targetObject
506
507 This property holds the \l{targetObject} part of the
508 \l{targetObject}/\l{targetProperty} pair that together can specify one of
509 the properties Synchronizer will synchronize.
510 */
511QObject *QQmlSynchronizer::targetObject() const
512{
513 Q_D(const QQmlSynchronizer);
514 return d->targetObjectProperty.object;
515}
516
517void QQmlSynchronizer::setTargetObject(QObject *object)
518{
519 Q_D(QQmlSynchronizer);
520 if (object == d->targetObjectProperty.object)
521 return;
522
523 if (d->isComponentFinalized)
524 d->disconnectObjectProperty(d->targetProperty, &d->targetObjectProperty);
525
526 d->targetObjectProperty.object = object;
527 emit targetObjectChanged();
528
529 if (d->isComponentFinalized)
530 d->connectObjectProperty(d->targetProperty, &d->targetObjectProperty, this);
531}
532
533/*!
534 \qmlproperty string Qt.labs.synchronizer::Synchronizer::targetProperty
535
536 This targetProperty holds the \l{targetProperty} part of the
537 \l{targetObject}/\l{targetProperty} pair that together can specify one of
538 the properties Synchronizer will synchronize.
539*/
540QString QQmlSynchronizer::targetProperty() const
541{
542 Q_D(const QQmlSynchronizer);
543 return d->targetProperty;
544}
545
546void QQmlSynchronizer::setTargetProperty(const QString &property)
547{
548 Q_D(QQmlSynchronizer);
549 if (property == d->targetProperty)
550 return;
551
552 if (d->isComponentFinalized)
553 d->disconnectObjectProperty(d->targetProperty, &d->targetObjectProperty);
554
555 d->targetProperty = property;
556 emit targetPropertyChanged();
557
558 if (d->isComponentFinalized)
559 d->connectObjectProperty(d->targetProperty, &d->targetObjectProperty, this);
560}
561
562/*!
563 \qmlsignal Qt.labs.synchronizer::Synchronizer::valueBounced(QtObject object, string property)
564
565 This signal is emitted if the \a{property} of the \a{object} refused
566 an attempt to set its value as part of the synchronization and produced a
567 different value in response. Such \e{bounced} values are ignored and
568 \e{don't} trigger another round of synchronization.
569 */
570
571/*!
572 \qmlsignal Qt.labs.synchronizer::Synchronizer::valueIgnored(QtObject object, string property)
573
574 This signal is emitted if \a{property} of the \a{object} did not
575 respond to an attempt to set its value as part of the synchronization.
576 */
577
578
579void QQmlSynchronizerPrivate::createConnection(
580 QQmlSynchronizer *q, const QQmlSynchronizerProperty &property)
581{
582 if (property.core()->notifiesViaBindable()) {
583 changeHandlers.push_back(QQmlSynchronizerChangeHandler(this, property));
584 } else if (const int notifyIndex = property.notifyIndex(); notifyIndex != -1) {
585 slotObjects.push_back(QQmlRefPointer(
586 new QQmlSynchronizerSlotObject(q, property),
587 QQmlRefPointer<QQmlSynchronizerSlotObject>::AddRef));
588 }
589}
590
592 const QString &property, OwnedTarget *objectProperty)
593{
594 QObject *object = objectProperty->object;
595 if (!object || property.isEmpty())
596 return;
597
598 QQmlPropertyData localCore;
599 const QQmlPropertyData *coreData = objectProperty->core
600 ? objectProperty->core.get()
601 : QQmlPropertyCache::property(object, property, {}, &localCore);
602 if (!coreData || coreData->isFunction())
603 return;
604
605 const QQmlSynchronizerProperty synchronizerProperty = QQmlSynchronizerProperty(object, coreData);
606 const auto slot = std::find_if(slotObjects.begin(), slotObjects.end(), [&](const auto &slot) {
607 return slot->contains(synchronizerProperty);
608 });
609
610 if (slot == slotObjects.end()) {
611 const auto handler
612 = std::find_if(changeHandlers.begin(), changeHandlers.end(), [&](const auto &handler) {
613 return handler.contains(synchronizerProperty);
614 });
615
616 Q_ASSERT(handler != changeHandlers.end());
617 changeHandlers.erase(handler);
618 } else {
619 slotObjects.erase(slot);
620 }
621
622 objectProperty->core.reset();
623 objectProperty->auxiliary.reset();
624}
625
627 const QString &property, OwnedTarget *objectProperty, QQmlSynchronizer *q)
628{
629 QObject *object = objectProperty->object;
630 if (!object || property.isEmpty())
632
633 QQmlPropertyData localCore;
634 const QQmlPropertyData *coreData = QQmlPropertyCache::property(object, property, {}, &localCore);
635 if (!coreData) {
636 qmlWarning(q) << "Target object has no property called " << property;
638 }
639
640 if (coreData->isFunction()) {
641 qmlWarning(q) << "Member " << property << " of target object is a function";
643 }
644
645 if (coreData == &localCore) {
646 objectProperty->core = std::make_unique<QQmlPropertyData>(std::move(localCore));
647 coreData = objectProperty->core.get();
648 }
649
650 const QQmlSynchronizerProperty synchronizerProperty(object, coreData);
651 createConnection(q, synchronizerProperty);
652 return synchronizerProperty;
653}
654
655QQmlSynchronizerProperty QQmlSynchronizerPrivate::connectTarget(QQmlSynchronizer *q)
656{
657 QObject *object = target.object;
658 if (!object)
659 return QQmlSynchronizerProperty();
660
661 const QQmlPropertyData *core = target.core.get();
662 Q_ASSERT(core->isValid());
663
664 if (const QQmlPropertyData *valueTypeData = target.auxiliary.get()) {
665 const QQmlSynchronizerProperty property(object, core, valueTypeData);
666 createConnection(q, property);
667 return property;
668 }
669
670 const QQmlSynchronizerProperty property(object, core);
671 createConnection(q, property);
672 return property;
673}
674
675void QQmlSynchronizerPrivate::initialize(QQmlSynchronizer *q)
676{
677 changeHandlers.clear();
678 slotObjects.clear();
679
680 QQmlSynchronizerProperty initializationSource = connectTarget(q);
681 if (QQmlSynchronizerProperty source = connectObjectProperty(
682 sourceProperty, &sourceObjectProperty, q); source.isValid()) {
683 initializationSource = source;
684 }
685 connectObjectProperty(targetProperty, &targetObjectProperty, q);
686
687 const QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(q);
688 Q_ASSERT(propertyCache);
689
690 const int propertyCount = propertyCache->propertyCount();
691 const int propertyOffset = QQmlSynchronizer::staticMetaObject.propertyCount();
692
693 bool foundSource = false;
694 for (int i = propertyOffset; i < propertyCount; ++i) {
695 const QQmlPropertyData *property = propertyCache->property(i);
696 if (!property->isAlias())
697 continue;
698
699 const QQmlSynchronizerProperty synchronizerProperty(q, property);
700 createConnection(q, synchronizerProperty);
701
702 if (!foundSource && property->name(q) == QLatin1String("source")) {
703 initializationSource = synchronizerProperty;
704 foundSource = true;
705 continue;
706 }
707 }
708
709 if (initializationSource.isValid())
710 synchronize(initializationSource);
711}
712
714{
715 const QVariant value = property.read();
716
717 if (currentState) {
718 // There can be interesting logic attached to the target property that causes it
719 // to change multiple times in a row. We only count the last change.
720 currentState->results[property] = (value == currentState->value) ? Accepted : Bounced;
721 return;
722 }
723
724 Q_Q(QQmlSynchronizer);
725
726 State state;
727 state.results[property] = Origin;
728 const auto guard = QScopedValueRollback(currentState, &state);
729
730 for (const auto &slotObject : slotObjects) {
731 if (slotObject->contains(property))
732 continue;
733 state.results[slotObject->property()] = Ignored;
734 state.value = slotObject->coerce(value, q);
735 slotObject->write(state.value);
736 }
737
738 for (const QQmlSynchronizerChangeHandler &changeHandler : changeHandlers) {
739 if (changeHandler.contains(property))
740 continue;
741 state.results[changeHandler.property()] = Ignored;
742 state.value = changeHandler.coerce(value, q);
743 changeHandler.write(state.value);
744 }
745
746
747 for (auto it = state.results.constBegin(), end = state.results.constEnd(); it != end; ++it) {
748 switch (*it) {
749 case Origin:
750 case Accepted:
751 break;
752 case Bounced: {
753 const QQmlSynchronizerProperty &key = it.key();
754 emit q->valueBounced(key.object(), key.name());
755 break;
756 }
757 case Ignored: {
758 const QQmlSynchronizerProperty &key = it.key();
759 emit q->valueIgnored(key.object(), key.name());
760 break;
761 }
762 }
763 }
764}
765
767 int which, QSlotObjectBase *self, QObject *r, void **a, bool *ret)
768{
769 Q_UNUSED(a);
770 QQmlSynchronizerSlotObject *synchronizerSlotObject
771 = static_cast<QQmlSynchronizerSlotObject *>(self);
772 switch (which) {
773 case Destroy:
774 delete synchronizerSlotObject;
775 break;
776 case Call: {
777 QQmlSynchronizer *q = static_cast<QQmlSynchronizer *>(r);
778 QQmlSynchronizerPrivate::get(q)->synchronize(synchronizerSlotObject->m_property);
779 break;
780 }
781 case Compare:
782 if (ret)
783 *ret = false;
784 break;
785 case NumOperations:
786 break;
787 }
788}
789
790static QVariant doReadProperty(QObject *object, const QQmlPropertyData *property)
791{
792 const QMetaType metaType = property->propType();
793 if (metaType == QMetaType::fromType<QVariant>()) {
794 QVariant content;
795 property->readProperty(object, &content);
796 return content;
797 }
798
799 QVariant content(metaType);
800 property->readProperty(object, content.data());
801 return content;
802}
803
805{
806 Q_ASSERT(m_object);
807 Q_ASSERT(m_core);
808
809 QVariant coreContent = doReadProperty(m_object.data(), m_core);
810
811 if (!m_valueTypeData)
812 return coreContent;
813
814 if (QQmlGadgetPtrWrapper *wrapper
815 = QQmlGadgetPtrWrapper::instance(qmlEngine(m_object), coreContent.metaType())) {
816 return doReadProperty(wrapper, m_valueTypeData);
817 }
818
819 QQmlGadgetPtrWrapper wrapper(QQmlMetaType::valueType(coreContent.metaType()));
820 return doReadProperty(&wrapper, m_valueTypeData);
821}
822
823void QQmlSynchronizerProperty::write(QVariant &&value) const
824{
825 Q_ASSERT(value.metaType() == metaType());
826
827 if (!m_object) // Was disconnected in some way or other
828 return;
829
830 if (!m_valueTypeData) {
831 m_core->writeProperty(m_object, value.data(), QQmlPropertyData::DontRemoveBinding);
832 return;
833 }
834
835 QVariant coreContent = doReadProperty(m_object, m_core);
836
837 if (QQmlGadgetPtrWrapper *wrapper
838 = QQmlGadgetPtrWrapper::instance(qmlEngine(m_object), coreContent.metaType())) {
839 m_valueTypeData->writeProperty(wrapper, value.data(), QQmlPropertyData::DontRemoveBinding);
840 m_core->writeProperty(m_object, coreContent.data(), QQmlPropertyData::DontRemoveBinding);
841 return;
842 }
843
844 QQmlGadgetPtrWrapper wrapper(QQmlMetaType::valueType(coreContent.metaType()));
845 m_valueTypeData->writeProperty(&wrapper, value.data(), QQmlPropertyData::DontRemoveBinding);
846 m_core->writeProperty(m_object, coreContent.data(), QQmlPropertyData::DontRemoveBinding);
847}
848
849QVariant QQmlSynchronizerProperty::coerce(const QVariant &source, QQmlSynchronizer *q) const
850{
851 Q_ASSERT(m_core);
852
853 const QMetaType targetMetaType = m_valueTypeData
854 ? m_valueTypeData->propType()
855 : m_core->propType();
856 const QMetaType sourceMetaType = source.metaType();
857 if (targetMetaType == sourceMetaType)
858 return source;
859
860 QVariant target(targetMetaType);
861
862 QQmlData *ddata = QQmlData::get(q);
863 if (ddata && !ddata->jsWrapper.isNullOrUndefined()) {
864 QV4::Scope scope(ddata->jsWrapper.engine());
865 QV4::ScopedValue scoped(scope, scope.engine->fromData(sourceMetaType, source.constData()));
866 if (QV4::ExecutionEngine::metaTypeFromJS(scoped, targetMetaType, target.data()))
867 return target;
868 }
869
870 if (QMetaType::convert(sourceMetaType, source.constData(), targetMetaType, target.data()))
871 return target;
872
873 qmlWarning(q) << "Cannot convert from " << sourceMetaType.name() << " to " << targetMetaType.name();
874 return target;
875}
876
878{
879 m_synchronizer->synchronize(m_property);
880}
881
882QT_END_NAMESPACE
QQmlSynchronizerChangeHandler(QQmlSynchronizerPrivate *receiver, const QQmlSynchronizerProperty &property)
QPropertyChangeHandler< QQmlSynchronizerHandler > createChangeHandler()
bool contains(const QQmlSynchronizerProperty &p) const
void write(QVariant value) const
QVariant coerce(const QVariant &source, QQmlSynchronizer *q) const
QQmlSynchronizerHandler(QQmlSynchronizerPrivate *receiver, const QQmlSynchronizerProperty &property)
QQmlSynchronizerProperty property() const
QQmlSynchronizerProperty connectObjectProperty(const QString &property, OwnedTarget *objectProperty, QQmlSynchronizer *q)
void initialize(QQmlSynchronizer *q)
void synchronize(const QQmlSynchronizerProperty &property)
std::vector< QQmlSynchronizerChangeHandler > changeHandlers
QQmlSynchronizerProperty connectTarget(QQmlSynchronizer *q)
static QQmlSynchronizerPrivate * get(QQmlSynchronizer *q)
std::vector< QQmlRefPointer< QQmlSynchronizerSlotObject > > slotObjects
void disconnectObjectProperty(const QString &property, OwnedTarget *objectProperty)
void createConnection(QQmlSynchronizer *q, const QQmlSynchronizerProperty &property)
\qmlsignal Qt.labs.synchronizer::Synchronizer::valueBounced(QtObject object, string property)
void write(QVariant value) const
QQmlSynchronizerProperty property() const
QVariant coerce(const QVariant &source, QQmlSynchronizer *q) const
bool contains(const QQmlSynchronizerProperty &p) const
static void impl(int which, QtPrivate::QSlotObjectBase *self, QObject *r, void **a, bool *ret)
QQmlSynchronizerSlotObject(QQmlSynchronizer *receiver, const QQmlSynchronizerProperty &property)
void operator()(QQmlSynchronizer *receiver)
static QVariant doReadProperty(QObject *object, const QQmlPropertyData *property)
std::unique_ptr< const QQmlPropertyData > core
std::unique_ptr< const QQmlPropertyData > auxiliary
QHash< QQmlSynchronizerProperty, Result > results
const QQmlPropertyData * valueTypeData() const
QQmlSynchronizerProperty()=default
QVariant coerce(const QVariant &source, QQmlSynchronizer *q) const
friend bool comparesEqual(const QQmlSynchronizerProperty &a, const QQmlSynchronizerProperty &b) noexcept
QQmlSynchronizerProperty(QObject *object, const QQmlPropertyData *core, const QQmlPropertyData *valueTypeData=nullptr)
void write(QVariant &&value) const
const QQmlPropertyData * core() const