7#include <private/qobject_p.h>
8#include <private/qqmlproperty_p.h>
9#include <private/qqmlvaluetype_p.h>
10#include <private/qv4scopedvalue_p.h>
12#include <QtQml/qqmlinfo.h>
13#include <QtCore/qcompare.h>
21 QObject *object,
const QQmlPropertyData *core,
22 const QQmlPropertyData *valueTypeData =
nullptr)
28 bool isValid()
const {
return m_object !=
nullptr && m_core !=
nullptr; }
31 void write(QVariant &&value)
const;
42 const QString coreName = m_core->name(m_object);
44 const QQmlPropertyData *vt = valueTypeData();
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);
61 m_valueTypeData =
nullptr;
67 QPointer<QObject> m_object;
68 const QQmlPropertyData *m_core =
nullptr;
69 const QQmlPropertyData *m_valueTypeData =
nullptr;
74 return a.m_core == b.m_core && a.m_valueTypeData == b.m_valueTypeData
75 && a.m_object == b.m_object;
108 QObject::disconnect(m_connection);
112 void write(QVariant value)
const { m_property.write(std::move(value)); }
115 return m_property.coerce(source, q);
119 static void impl(
int which, QtPrivate::QSlotObjectBase *self, QObject *r,
void **a,
bool *ret);
122 impl(QtPrivate::QSlotObjectBase::Call,
this, receiver,
nullptr,
nullptr);
129 QMetaObject::Connection createConnection(QQmlSynchronizer *receiver)
132 QObject *object = m_property.object();
134 const int notifyIndex = m_property.notifyIndex();
135 Q_ASSERT(notifyIndex != -1);
137 return QObjectPrivate::connectImpl(
138 object, notifyIndex, receiver,
nullptr,
this, Qt::AutoConnection,
nullptr,
139 object->metaObject());
144 QMetaObject::Connection m_connection;
153 , m_synchronizer(receiver)
157 void write(QVariant value)
const { m_property.write(std::move(value)); }
160 return m_property.coerce(source, q);
169 const QQmlPropertyData *core = m_property.core();
170 Q_ASSERT(core && core->isValid());
171 QObject *object = m_property.object();
174 QUntypedBindable bindable;
175 void *argv[1] { &bindable };
176 core->doMetacall<QMetaObject::BindableProperty>(object, core->coreIndex(), argv);
177 Q_ASSERT(bindable.isValid());
179 return bindable.onValueChanged(*
this);
197 QPropertyChangeHandler<QQmlSynchronizerHandler> changeHandler;
202 Q_DECLARE_PUBLIC(QQmlSynchronizer)
254 const QString &property,
OwnedTarget *objectProperty, QQmlSynchronizer *q);
264
265
266
267
268
269
270
271
272
273
274
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
418QQmlSynchronizer::QQmlSynchronizer(QObject *parent)
419 : QObject(*(
new QQmlSynchronizerPrivate), parent)
423void QQmlSynchronizer::setTarget(
const QQmlProperty &target)
425 Q_D(QQmlSynchronizer);
428 Q_ASSERT(!d->isComponentFinalized);
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)
438void QQmlSynchronizer::componentFinalized()
440 Q_D(QQmlSynchronizer);
442 d->isComponentFinalized =
true;
447
448
449
450
451
452
453QObject *QQmlSynchronizer::sourceObject()
const
455 Q_D(
const QQmlSynchronizer);
456 return d->sourceObjectProperty.object;
459void QQmlSynchronizer::setSourceObject(QObject *object)
461 Q_D(QQmlSynchronizer);
462 if (object == d->sourceObjectProperty.object)
465 if (d->isComponentFinalized)
466 d->disconnectObjectProperty(d->sourceProperty, &d->sourceObjectProperty);
468 d->sourceObjectProperty.object = object;
469 emit sourceObjectChanged();
471 if (d->isComponentFinalized)
472 d->connectObjectProperty(d->sourceProperty, &d->sourceObjectProperty,
this);
476
477
478
479
480
481
482QString QQmlSynchronizer::sourceProperty()
const
484 Q_D(
const QQmlSynchronizer);
485 return d->sourceProperty;
488void QQmlSynchronizer::setSourceProperty(
const QString &property)
490 Q_D(QQmlSynchronizer);
491 if (property == d->sourceProperty)
494 if (d->isComponentFinalized)
495 d->disconnectObjectProperty(d->sourceProperty, &d->sourceObjectProperty);
497 d->sourceProperty = property;
498 emit sourcePropertyChanged();
500 if (d->isComponentFinalized)
501 d->connectObjectProperty(d->sourceProperty, &d->sourceObjectProperty,
this);
505
506
507
508
509
510
511QObject *QQmlSynchronizer::targetObject()
const
513 Q_D(
const QQmlSynchronizer);
514 return d->targetObjectProperty.object;
517void QQmlSynchronizer::setTargetObject(QObject *object)
519 Q_D(QQmlSynchronizer);
520 if (object == d->targetObjectProperty.object)
523 if (d->isComponentFinalized)
524 d->disconnectObjectProperty(d->targetProperty, &d->targetObjectProperty);
526 d->targetObjectProperty.object = object;
527 emit targetObjectChanged();
529 if (d->isComponentFinalized)
530 d->connectObjectProperty(d->targetProperty, &d->targetObjectProperty,
this);
534
535
536
537
538
539
540QString QQmlSynchronizer::targetProperty()
const
542 Q_D(
const QQmlSynchronizer);
543 return d->targetProperty;
546void QQmlSynchronizer::setTargetProperty(
const QString &property)
548 Q_D(QQmlSynchronizer);
549 if (property == d->targetProperty)
552 if (d->isComponentFinalized)
553 d->disconnectObjectProperty(d->targetProperty, &d->targetObjectProperty);
555 d->targetProperty = property;
556 emit targetPropertyChanged();
558 if (d->isComponentFinalized)
559 d->connectObjectProperty(d->targetProperty, &d->targetObjectProperty,
this);
563
564
565
566
567
568
569
572
573
574
575
576
580 QQmlSynchronizer *q,
const QQmlSynchronizerProperty &property)
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));
592 const QString &property,
OwnedTarget *objectProperty)
594 QObject *object = objectProperty->object;
595 if (!object || property.isEmpty())
598 QQmlPropertyData localCore;
599 const QQmlPropertyData *coreData = objectProperty->core
600 ? objectProperty->core.get()
601 : QQmlPropertyCache::property(object, property, {}, &localCore);
602 if (!coreData || coreData->isFunction())
606 const auto slot = std::find_if(slotObjects.begin(), slotObjects.end(), [&](
const auto &slot) {
607 return slot->contains(synchronizerProperty);
610 if (slot == slotObjects.end()) {
612 = std::find_if(changeHandlers.begin(), changeHandlers.end(), [&](
const auto &handler) {
613 return handler.contains(synchronizerProperty);
616 Q_ASSERT(handler != changeHandlers.end());
617 changeHandlers.erase(handler);
619 slotObjects.erase(slot);
622 objectProperty->core.reset();
623 objectProperty->auxiliary.reset();
627 const QString &property,
OwnedTarget *objectProperty, QQmlSynchronizer *q)
629 QObject *object = objectProperty->object;
630 if (!object || property.isEmpty())
633 QQmlPropertyData localCore;
634 const QQmlPropertyData *coreData = QQmlPropertyCache::property(object, property, {}, &localCore);
636 qmlWarning(q) <<
"Target object has no property called " << property;
640 if (coreData->isFunction()) {
641 qmlWarning(q) <<
"Member " << property <<
" of target object is a function";
645 if (coreData == &localCore) {
646 objectProperty->core = std::make_unique<QQmlPropertyData>(std::move(localCore));
647 coreData = objectProperty->core.get();
651 createConnection(q, synchronizerProperty);
652 return synchronizerProperty;
657 QObject *object = target.object;
659 return QQmlSynchronizerProperty();
661 const QQmlPropertyData *core = target.core.get();
662 Q_ASSERT(core->isValid());
664 if (
const QQmlPropertyData *valueTypeData = target.auxiliary.get()) {
665 const QQmlSynchronizerProperty property(object, core, valueTypeData);
666 createConnection(q, property);
670 const QQmlSynchronizerProperty property(object, core);
671 createConnection(q, property);
677 changeHandlers.clear();
680 QQmlSynchronizerProperty initializationSource = connectTarget(q);
681 if (QQmlSynchronizerProperty source = connectObjectProperty(
682 sourceProperty, &sourceObjectProperty, q); source.isValid()) {
683 initializationSource = source;
685 connectObjectProperty(targetProperty, &targetObjectProperty, q);
687 const QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(q);
688 Q_ASSERT(propertyCache);
690 const int propertyCount = propertyCache->propertyCount();
691 const int propertyOffset = QQmlSynchronizer::staticMetaObject.propertyCount();
693 bool foundSource =
false;
694 for (
int i = propertyOffset; i < propertyCount; ++i) {
695 const QQmlPropertyData *property = propertyCache->property(i);
696 if (!property->isAlias())
699 const QQmlSynchronizerProperty synchronizerProperty(q, property);
700 createConnection(q, synchronizerProperty);
702 if (!foundSource && property->name(q) == QLatin1String(
"source")) {
703 initializationSource = synchronizerProperty;
709 if (initializationSource.isValid())
710 synchronize(initializationSource);
715 const QVariant value = property.read();
720 currentState->results[property] = (value == currentState->value) ? Accepted : Bounced;
724 Q_Q(QQmlSynchronizer);
727 state.results[property] = Origin;
728 const auto guard = QScopedValueRollback(
currentState, &state);
730 for (
const auto &slotObject : slotObjects) {
731 if (slotObject->contains(property))
733 state.results[slotObject->property()] = Ignored;
734 state.value = slotObject->coerce(value, q);
735 slotObject->write(state.value);
738 for (
const QQmlSynchronizerChangeHandler &changeHandler : changeHandlers) {
739 if (changeHandler.contains(property))
741 state.results[changeHandler.property()] = Ignored;
742 state.value = changeHandler.coerce(value, q);
743 changeHandler.write(state.value);
747 for (
auto it = state.results.constBegin(), end = state.results.constEnd(); it != end; ++it) {
754 emit q->valueBounced(key.object(), key.name());
759 emit q->valueIgnored(key.object(), key.name());
767 int which, QSlotObjectBase *self, QObject *r,
void **a,
bool *ret)
774 delete synchronizerSlotObject;
777 QQmlSynchronizer *q =
static_cast<QQmlSynchronizer *>(r);
778 QQmlSynchronizerPrivate::get(q)->synchronize(synchronizerSlotObject->m_property);
792 const QMetaType metaType = property->propType();
793 if (metaType == QMetaType::fromType<QVariant>()) {
795 property->readProperty(object, &content);
799 QVariant content(metaType);
800 property->readProperty(object, content.data());
809 QVariant coreContent = doReadProperty(m_object.data(), m_core);
811 if (!m_valueTypeData)
814 if (QQmlGadgetPtrWrapper *wrapper
815 = QQmlGadgetPtrWrapper::instance(qmlEngine(m_object), coreContent.metaType())) {
816 return doReadProperty(wrapper, m_valueTypeData);
819 QQmlGadgetPtrWrapper wrapper(QQmlMetaType::valueType(coreContent.metaType()));
820 return doReadProperty(&wrapper, m_valueTypeData);
825 Q_ASSERT(value.metaType() == metaType());
830 if (!m_valueTypeData) {
831 m_core->writeProperty(m_object, value.data(), QQmlPropertyData::DontRemoveBinding);
835 QVariant coreContent = doReadProperty(m_object, m_core);
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);
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);
853 const QMetaType targetMetaType = m_valueTypeData
854 ? m_valueTypeData->propType()
855 : m_core->propType();
856 const QMetaType sourceMetaType = source.metaType();
857 if (targetMetaType == sourceMetaType)
860 QVariant target(targetMetaType);
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()))
870 if (QMetaType::convert(sourceMetaType, source.constData(), targetMetaType, target.data()))
873 qmlWarning(q) <<
"Cannot convert from " << sourceMetaType.name() <<
" to " << targetMetaType.name();
879 m_synchronizer->synchronize(m_property);
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
OwnedTarget sourceObjectProperty
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)
bool isComponentFinalized
OwnedTarget targetObjectProperty
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
~QQmlSynchronizerSlotObject()
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
QPointer< QObject > object
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