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