1// Copyright (C) 2016 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
4#include "qquickpropertychanges_p.h"
5
6#include <private/qqmlopenmetaobject_p.h>
7#include <private/qqmlengine_p.h>
8
9#include <qqmlinfo.h>
10#include <private/qqmlcustomparser_p.h>
11#include <qqmlexpression.h>
12#include <private/qqmlbinding_p.h>
13#include <qqmlcontext.h>
14#include <private/qqmlproperty_p.h>
15#include <private/qqmlcontext_p.h>
16#include <private/qquickstate_p_p.h>
17#include <private/qqmlboundsignal_p.h>
18#include <private/qv4qmlcontext_p.h>
19#include <private/qqmlpropertybinding_p.h>
20#include <private/qqmlirbuilder_p.h>
21
22#include <QtCore/qdebug.h>
23
24#include <private/qobject_p.h>
25
26QT_BEGIN_NAMESPACE
27
28/*!
29 \qmltype PropertyChanges
30 \inqmlmodule QtQuick
31 \ingroup qtquick-states
32 \brief Describes new property bindings or values for a state.
33
34 PropertyChanges is used to define the property values or bindings in a
35 \l State. This enables an item's property values to be changed when it
36 \l {Qt Quick States}{changes between states}.
37
38 To create a PropertyChanges object, bind to properties of the target
39 item like you would bind to local properties. This way you can define
40 the new property values or bindings. For example:
41
42 \snippet qml/propertychanges.qml import
43 \codeline
44 \snippet qml/propertychanges.qml 0
45
46 When the mouse is pressed, the \l Rectangle changes to the \e resized
47 state. In this state, the PropertyChanges object sets the rectangle's
48 color to blue and the \c height value to that of \c container.height.
49
50 Note this automatically binds \c rect.height to \c container.height
51 in the \e resized state. If a property binding should not be
52 established, and the height should just be set to the value of
53 \c container.height at the time of the state change, set the \l explicit
54 property to \c true.
55
56 A PropertyChanges object can also override the default signal handler
57 for an object to implement a signal handler specific to the new state:
58
59 \qml
60 PropertyChanges {
61 myMouseArea.onClicked: doSomethingDifferent()
62 }
63 \endqml
64
65 \note PropertyChanges can be used to change anchor margins, but not other anchor
66 values; use AnchorChanges for this instead. Similarly, to change an \l Item's
67 \l {Item::}{parent} value, use ParentChange instead.
68
69
70 \section2 Resetting Property Values
71
72 The \c undefined value can be used to reset the property value for a state.
73 In the following example, when \c myText changes to the \e widerText
74 state, its \c width property is reset, giving the text its natural width
75 and displaying the whole string on a single line.
76
77 \snippet qml/propertychanges.qml reset
78
79
80 \section2 Immediate Property Changes in Transitions
81
82 When \l{Animation and Transitions in Qt Quick}{Transitions} are used to animate
83 state changes, they animate properties from their values in the current
84 state to those defined in the new state (as defined by PropertyChanges
85 objects). However, it is sometimes desirable to set a property value
86 \e immediately during a \l Transition, without animation; in these cases,
87 the PropertyAction type can be used to force an immediate property
88 change.
89
90 See the PropertyAction documentation for more details.
91
92 \note The \l{Item::}{visible} and \l{Item::}{enabled} properties of \l Item do not behave
93 exactly the same as other properties in PropertyChanges. Since these properties can be
94 changed implicitly through their parent's state, they should be set explicitly in all PropertyChanges.
95 An item will still not be enabled/visible if one of its parents is not enabled or visible.
96
97 \note For backwards compatibility with Qt 5, you can also specify PropertyChanges using
98 the \l target property and plain property names without IDs. For example:
99 \c {PropertyChanges { target: myItem; x: 15 }}. The form with ID instead of \l target
100 is recommended. You may also need to use the form with \l target if the file is to be
101 edited with \l{Qt Design Studio}. Mind that \l{Qt Design Studio} also imposes a number
102 of further restrictions on the files it can work with.
103
104 \sa {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States}, {Qt QML}
105*/
106
107/*!
108 \qmlproperty QtObject QtQuick::PropertyChanges::target
109 This property holds the object which contains the properties to be changed.
110
111 \note You generally don't have to use this property. It only exists for
112 compatibility with Qt 5 and for compatibility with \l{Qt Design Studio}.
113*/
114
115class QQuickReplaceSignalHandler : public QQuickStateActionEvent
116{
117public:
118 QQuickReplaceSignalHandler() {}
119 ~QQuickReplaceSignalHandler() {}
120
121 EventType type() const override { return SignalHandler; }
122
123 QQmlProperty property;
124 QQmlRefPointer<QQmlBoundSignalExpression> expression;
125 QQmlRefPointer<QQmlBoundSignalExpression> reverseExpression;
126 QQmlRefPointer<QQmlBoundSignalExpression> rewindExpression;
127
128 void execute() override {
129 QQmlPropertyPrivate::setSignalExpression(that: property, expression.data());
130 }
131
132 bool isReversable() override { return true; }
133 void reverse() override {
134 QQmlPropertyPrivate::setSignalExpression(that: property, reverseExpression.data());
135 }
136
137 void saveOriginals() override {
138 saveCurrentValues();
139 reverseExpression = rewindExpression;
140 }
141
142 bool needsCopy() override { return true; }
143 void copyOriginals(QQuickStateActionEvent *other) override
144 {
145 QQuickReplaceSignalHandler *rsh = static_cast<QQuickReplaceSignalHandler*>(other);
146 saveCurrentValues();
147 if (rsh == this)
148 return;
149 reverseExpression = rsh->reverseExpression;
150 }
151
152 void rewind() override {
153 QQmlPropertyPrivate::setSignalExpression(that: property, rewindExpression.data());
154 }
155 void saveCurrentValues() override {
156 rewindExpression = QQmlPropertyPrivate::signalExpression(that: property);
157 }
158
159 bool mayOverride(QQuickStateActionEvent *other) override {
160 if (other == this)
161 return true;
162 if (other->type() != type())
163 return false;
164 if (static_cast<QQuickReplaceSignalHandler*>(other)->property == property)
165 return true;
166 return false;
167 }
168};
169
170
171class QQuickPropertyChangesPrivate : public QQuickStateOperationPrivate
172{
173 Q_DECLARE_PUBLIC(QQuickPropertyChanges)
174public:
175 QQuickPropertyChangesPrivate() : decoded(true), restore(true),
176 isExplicit(false) {}
177
178 QPointer<QObject> object;
179 QList<const QV4::CompiledData::Binding *> bindings;
180 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
181
182 bool decoded : 1;
183 bool restore : 1;
184 bool isExplicit : 1;
185
186 void decode();
187 void decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlUnit, const QV4::CompiledData::Binding *binding);
188
189 class ExpressionChange {
190 public:
191 ExpressionChange(const QString &_name,
192 const QV4::CompiledData::Binding *_binding,
193 QQmlBinding::Identifier _id,
194 const QString& _expr,
195 const QUrl &_url,
196 int _line,
197 int _column)
198 : name(_name), binding(_binding), id(_id), expression(_expr), url(_url), line(_line), column(_column) {}
199 QString name;
200 const QV4::CompiledData::Binding *binding;
201 QQmlBinding::Identifier id;
202 QString expression;
203 QUrl url;
204 int line;
205 int column;
206 };
207
208 QList<QPair<QString, QVariant> > properties;
209 QList<ExpressionChange> expressions;
210 QList<QQuickReplaceSignalHandler*> signalReplacements;
211
212 QQmlProperty property(const QString &);
213};
214
215void QQuickPropertyChangesParser::verifyList(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
216{
217 switch (binding->type()) {
218 case QV4::CompiledData::Binding::Type_Object:
219 error(object: compilationUnit->objectAt(index: binding->value.objectIndex),
220 description: QQuickPropertyChanges::tr(
221 s: "PropertyChanges does not support creating state-specific objects."));
222 break;
223 case QV4::CompiledData::Binding::Type_GroupProperty:
224 case QV4::CompiledData::Binding::Type_AttachedProperty: {
225 const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(index: binding->value.objectIndex);
226 const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
227 for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding)
228 verifyList(compilationUnit, binding: subBinding);
229 break;
230 }
231 default:
232 break;
233 }
234}
235
236void QQuickPropertyChangesPrivate::decode()
237{
238 if (decoded)
239 return;
240
241 for (const QV4::CompiledData::Binding *binding : std::as_const(t&: bindings))
242 decodeBinding(propertyPrefix: QString(), qmlUnit: compilationUnit, binding);
243
244 bindings.clear();
245
246 decoded = true;
247}
248
249void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
250{
251 Q_Q(QQuickPropertyChanges);
252
253 QString propertyName = propertyPrefix + compilationUnit->stringAt(index: binding->propertyNameIndex);
254
255 switch (binding->type()) {
256 case QV4::CompiledData::Binding::Type_GroupProperty:
257 case QV4::CompiledData::Binding::Type_AttachedProperty: {
258 QString pre = propertyName + QLatin1Char('.');
259 const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(index: binding->value.objectIndex);
260 const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
261 for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
262 decodeBinding(propertyPrefix: pre, compilationUnit, binding: subBinding);
263 }
264 return;
265 }
266 default:
267 break;
268 }
269
270 if (binding->isSignalHandler() || QmlIR::IRBuilder::isSignalPropertyName(name: propertyName)) {
271 QQmlProperty prop = property(propertyName);
272 if (prop.isSignalProperty()) {
273 QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler;
274 handler->property = prop;
275 handler->expression.adopt(
276 new QQmlBoundSignalExpression(
277 prop.object(), QQmlPropertyPrivate::get(p: prop)->signalIndex(),
278 QQmlContextData::get(context: qmlContext(q)), prop.object(),
279 compilationUnit->runtimeFunctions.at(i: binding->value.compiledScriptIndex)));
280 signalReplacements << handler;
281 return;
282 }
283 }
284
285 if (binding->type() == QV4::CompiledData::Binding::Type_Script
286 || binding->isTranslationBinding()) {
287 QUrl url = QUrl();
288 int line = -1;
289 int column = -1;
290
291 QQmlData *ddata = QQmlData::get(object: q);
292 if (ddata && ddata->outerContext && !ddata->outerContext->url().isEmpty()) {
293 url = ddata->outerContext->url();
294 line = ddata->lineNumber;
295 column = ddata->columnNumber;
296 }
297
298 QString expression;
299 QQmlBinding::Identifier id = QQmlBinding::Invalid;
300
301 if (!binding->isTranslationBinding()) {
302 expression = compilationUnit->bindingValueAsString(binding);
303 id = binding->value.compiledScriptIndex;
304 }
305 expressions << ExpressionChange(propertyName, binding, id, expression, url, line, column);
306 return;
307 }
308
309 QVariant var;
310 switch (binding->type()) {
311 case QV4::CompiledData::Binding::Type_Script:
312 case QV4::CompiledData::Binding::Type_Translation:
313 case QV4::CompiledData::Binding::Type_TranslationById:
314 Q_UNREACHABLE();
315 case QV4::CompiledData::Binding::Type_String:
316 var = compilationUnit->bindingValueAsString(binding);
317 break;
318 case QV4::CompiledData::Binding::Type_Number:
319 var = compilationUnit->bindingValueAsNumber(binding);
320 break;
321 case QV4::CompiledData::Binding::Type_Boolean:
322 var = binding->valueAsBoolean();
323 break;
324 case QV4::CompiledData::Binding::Type_Null:
325 var = QVariant::fromValue(value: nullptr);
326 break;
327 default:
328 break;
329 }
330
331 properties << qMakePair(value1&: propertyName, value2&: var);
332}
333
334void QQuickPropertyChangesParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
335{
336 for (int ii = 0; ii < props.size(); ++ii)
337 verifyList(compilationUnit, binding: props.at(i: ii));
338}
339
340void QQuickPropertyChangesParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
341{
342 QQuickPropertyChangesPrivate *p =
343 static_cast<QQuickPropertyChangesPrivate *>(QObjectPrivate::get(o: obj));
344 p->bindings = bindings;
345 p->compilationUnit = compilationUnit;
346 p->decoded = false;
347
348 QQmlData *data = QQmlData::get(object: obj);
349 Q_ASSERT(data && !data->wasDeleted(obj));
350 data->releaseDeferredData();
351}
352
353QQuickPropertyChanges::QQuickPropertyChanges()
354: QQuickStateOperation(*(new QQuickPropertyChangesPrivate))
355{
356}
357
358QQuickPropertyChanges::~QQuickPropertyChanges()
359{
360 Q_D(QQuickPropertyChanges);
361 for(int ii = 0; ii < d->signalReplacements.size(); ++ii)
362 delete d->signalReplacements.at(i: ii);
363}
364
365QObject *QQuickPropertyChanges::object() const
366{
367 Q_D(const QQuickPropertyChanges);
368 return d->object;
369}
370
371void QQuickPropertyChanges::setObject(QObject *o)
372{
373 Q_D(QQuickPropertyChanges);
374 if (o != d->object) {
375 d->object = o;
376 emit objectChanged();
377 }
378}
379
380/*!
381 \qmlproperty bool QtQuick::PropertyChanges::restoreEntryValues
382
383 This property holds whether the previous values should be restored when
384 leaving the state.
385
386 The default value is \c true. Setting this value to \c false creates a
387 temporary state that has permanent effects on property values.
388*/
389bool QQuickPropertyChanges::restoreEntryValues() const
390{
391 Q_D(const QQuickPropertyChanges);
392 return d->restore;
393}
394
395void QQuickPropertyChanges::setRestoreEntryValues(bool v)
396{
397 Q_D(QQuickPropertyChanges);
398 if (v != d->restore) {
399 d->restore = v;
400 emit restoreEntryValuesChanged();
401 }
402}
403
404QQmlProperty
405QQuickPropertyChangesPrivate::property(const QString &property)
406{
407 Q_Q(QQuickPropertyChanges);
408 QQmlData *ddata = QQmlData::get(object: q);
409 QQmlProperty prop = QQmlPropertyPrivate::create(
410 target: object, propertyName: property, context: ddata ? ddata->outerContext : QQmlRefPointer<QQmlContextData>(),
411 flags: QQmlPropertyPrivate::InitFlag::AllowId | QQmlPropertyPrivate::InitFlag::AllowSignal);
412 if (!prop.isValid()) {
413 qmlWarning(me: q) << QQuickPropertyChanges::tr(s: "Cannot assign to non-existent property \"%1\"").arg(a: property);
414 return QQmlProperty();
415 } else if (!(prop.type() & QQmlProperty::SignalProperty) && !prop.isWritable()) {
416 qmlWarning(me: q) << QQuickPropertyChanges::tr(s: "Cannot assign to read-only property \"%1\"").arg(a: property);
417 return QQmlProperty();
418 }
419 return prop;
420}
421
422QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions()
423{
424 Q_D(QQuickPropertyChanges);
425
426 d->decode();
427
428 ActionList list;
429
430 for (int ii = 0; ii < d->properties.size(); ++ii) {
431 QQmlProperty prop = d->property(property: d->properties.at(i: ii).first);
432
433 QQuickStateAction a(d->object, prop, d->properties.at(i: ii).first,
434 d->properties.at(i: ii).second);
435
436 if (a.property.isValid()) {
437 a.restore = restoreEntryValues();
438 list << a;
439 }
440 }
441
442 for (int ii = 0; ii < d->signalReplacements.size(); ++ii) {
443 QQuickReplaceSignalHandler *handler = d->signalReplacements.at(i: ii);
444
445 if (handler->property.isValid()) {
446 QQuickStateAction a;
447 a.event = handler;
448 list << a;
449 }
450 }
451
452 for (int ii = 0; ii < d->expressions.size(); ++ii) {
453
454 QQuickPropertyChangesPrivate::ExpressionChange e = d->expressions.at(i: ii);
455 const QString &property = e.name;
456 QQmlProperty prop = d->property(property);
457
458 if (prop.isValid()) {
459 QQuickStateAction a;
460 a.restore = restoreEntryValues();
461 a.property = prop;
462 a.fromValue = a.property.read();
463 a.specifiedObject = d->object;
464 a.specifiedProperty = property;
465
466 QQmlRefPointer<QQmlContextData> context = QQmlContextData::get(context: qmlContext(this));
467 QV4::Scope scope(qmlEngine(this)->handle());
468 QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, QV4::QmlContext::create(parent: scope.engine->rootContext(), context, scopeObject: object()));
469
470 if (d->isExplicit) {
471 // in this case, we don't want to assign a binding, per se,
472 // so we evaluate the expression and assign the result.
473 // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
474 // so that we can avoid creating then destroying the binding in this case.
475 std::unique_ptr<QQmlBinding> newBinding = nullptr;
476 if (e.binding && e.binding->isTranslationBinding()) {
477 newBinding.reset(p: QQmlBinding::createTranslationBinding(unit: d->compilationUnit, binding: e.binding, obj: object(), ctxt: context));
478 } else if (e.id != QQmlBinding::Invalid) {
479 newBinding.reset(p: QQmlBinding::create(property: &QQmlPropertyPrivate::get(p: prop)->core, function: d->compilationUnit->runtimeFunctions.at(i: e.id), obj: object(), ctxt: context, scope: qmlCtxt));
480 } else {
481 newBinding.reset(p: QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, e.expression, object(), context, url: e.url.toString(), lineNumber: e.line));
482 }
483 a.toValue = newBinding->evaluate();
484 } else {
485 QQmlAnyBinding newBinding = nullptr;
486 if (e.binding && e.binding->isTranslationBinding()) {
487 newBinding = QQmlAnyBinding::createTranslationBinding(prop, compilationUnit: d->compilationUnit, translationBinding: e.binding, scopeObject: object(), context);
488 } else if (e.id != QQmlBinding::Invalid) {
489 newBinding = QQmlAnyBinding::createFromFunction(prop,
490 function: d->compilationUnit->runtimeFunctions.at(i: e.id),
491 obj: object(), ctxt: context, scope: qmlCtxt);
492 } else {
493 newBinding = QQmlAnyBinding::createFromCodeString(prop, code: e.expression, obj: object(), ctxt: context, url: e.url.toString(), lineNumber: e.line);
494 }
495
496 a.toBinding = newBinding;
497 a.deletableToBinding = true;
498 }
499
500 list << a;
501 }
502 }
503
504 return list;
505}
506
507/*!
508 \qmlproperty bool QtQuick::PropertyChanges::explicit
509
510 If explicit is set to true, any potential bindings will be interpreted as
511 once-off assignments that occur when the state is entered.
512
513 In the following example, the addition of explicit prevents \c myItem.width from
514 being bound to \c parent.width. Instead, it is assigned the value of \c parent.width
515 at the time of the state change.
516 \qml
517 PropertyChanges {
518 target: myItem
519 explicit: true
520 width: parent.width
521 }
522 \endqml
523
524 By default, explicit is false.
525*/
526bool QQuickPropertyChanges::isExplicit() const
527{
528 Q_D(const QQuickPropertyChanges);
529 return d->isExplicit;
530}
531
532void QQuickPropertyChanges::setIsExplicit(bool e)
533{
534 Q_D(QQuickPropertyChanges);
535 if (e != d->isExplicit) {
536 d->isExplicit = e;
537 emit isExplicitChanged();
538 }
539}
540
541bool QQuickPropertyChanges::containsValue(const QString &name) const
542{
543 Q_D(const QQuickPropertyChanges);
544 typedef QPair<QString, QVariant> PropertyEntry;
545
546 for (const PropertyEntry &entry : d->properties) {
547 if (entry.first == name) {
548 return true;
549 }
550 }
551
552 return false;
553}
554
555bool QQuickPropertyChanges::containsExpression(const QString &name) const
556{
557 Q_D(const QQuickPropertyChanges);
558 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
559
560 for (const ExpressionEntry &entry : d->expressions) {
561 if (entry.name == name) {
562 return true;
563 }
564 }
565
566 return false;
567}
568
569bool QQuickPropertyChanges::containsProperty(const QString &name) const
570{
571 return containsValue(name) || containsExpression(name);
572}
573
574void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &value)
575{
576 Q_D(QQuickPropertyChanges);
577 typedef QPair<QString, QVariant> PropertyEntry;
578
579 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
580 if (it->name == name) {
581 d->expressions.erase(pos: it);
582 if (state() && state()->isStateActive()) {
583 QQmlPropertyPrivate::removeBinding(that: d->property(property: name));
584 d->property(property: name).write(value);
585 }
586
587 d->properties.append(t: PropertyEntry(name, value));
588 return;
589 }
590 }
591
592 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
593 if (it->first == name) {
594 it->second = value;
595 if (state() && state()->isStateActive())
596 d->property(property: name).write(value);
597 return;
598 }
599 }
600
601 QQuickStateAction action;
602 action.restore = restoreEntryValues();
603 action.property = d->property(property: name);
604 action.fromValue = action.property.read();
605 action.specifiedObject = object();
606 action.specifiedProperty = name;
607 action.toValue = value;
608
609 d->properties.append(t: PropertyEntry(name, value));
610 if (state() && state()->isStateActive()) {
611 state()->addEntryToRevertList(action);
612 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(that: action.property);
613 if (oldBinding)
614 oldBinding->setEnabled(e: false, f: QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
615 d->property(property: name).write(value);
616 }
617}
618
619void QQuickPropertyChanges::changeExpression(const QString &name, const QString &expression)
620{
621 Q_D(QQuickPropertyChanges);
622 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
623
624 bool hadValue = false;
625
626 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
627 if (it->first == name) {
628 d->properties.erase(pos: it);
629 hadValue = true;
630 break;
631 }
632 }
633
634 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
635 if (it->name == name) {
636 it->expression = expression;
637 if (state() && state()->isStateActive()) {
638 auto prop = d->property(property: name);
639 auto context = QQmlContextData::get(context: qmlContext(this));
640 QString url;
641 int lineNumber = 0;
642 QQmlAnyBinding::createFromCodeString(prop, code: expression, obj: object(), ctxt: context, url, lineNumber).installOn(target: prop);
643 }
644 return;
645 }
646 }
647
648 // adding a new expression.
649 d->expressions.append(t: ExpressionEntry(name, nullptr, QQmlBinding::Invalid, expression, QUrl(), -1, -1));
650
651 const QString url;
652 const quint16 lineNumber = 0;
653 if (state() && state()->isStateActive()) {
654 if (hadValue) {
655 auto prop = d->property(property: name);
656 QQmlAnyBinding oldBinding = QQmlAnyBinding::takeFrom(prop);
657 if (oldBinding)
658 state()->changeBindingInRevertList(target: object(), name, binding: oldBinding);
659
660 QQmlAnyBinding::createFromCodeString(prop, code: expression, obj: object(), ctxt: QQmlContextData::get(context: qmlContext(this)), url, lineNumber).installOn(target: prop);
661 } else {
662 QQuickStateAction action;
663 action.restore = restoreEntryValues();
664 action.property = d->property(property: name);
665 action.fromValue = action.property.read();
666 action.specifiedObject = object();
667 action.specifiedProperty = name;
668
669 QQmlAnyBinding newBinding;
670 if (d->isExplicit) {
671 newBinding = QQmlBinding::create(
672 &QQmlPropertyPrivate::get(p: action.property)->core, expression,
673 object(), QQmlContextData::get(context: qmlContext(this)));
674 } else {
675 const auto prop = action.property;
676 const auto context = QQmlContextData::get(context: qmlContext(this));
677 newBinding = QQmlAnyBinding::createFromCodeString(prop, code: expression, obj: object(), ctxt: context, url, lineNumber);
678 }
679 if (d->isExplicit) {
680 // don't assign the binding, merely evaluate the expression.
681 // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
682 // so that we can avoid creating then destroying the binding in this case.
683 action.toValue = static_cast<QQmlBinding *>(newBinding.asAbstractBinding())->evaluate();
684 } else {
685 // TODO: replace binding would be more efficient for new-style properties
686 QQmlAnyBinding::removeBindingFrom(prop&: action.property);
687 newBinding.installOn(target: action.property);
688 action.toBinding = newBinding;
689 action.deletableToBinding = true;
690 state()->addEntryToRevertList(action);
691 }
692 }
693 }
694 // what about the signal handler?
695}
696
697QVariant QQuickPropertyChanges::property(const QString &name) const
698{
699 Q_D(const QQuickPropertyChanges);
700 typedef QPair<QString, QVariant> PropertyEntry;
701 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
702
703 for (const PropertyEntry &entry : d->properties) {
704 if (entry.first == name) {
705 return entry.second;
706 }
707 }
708
709 for (const ExpressionEntry &entry : d->expressions) {
710 if (entry.name == name) {
711 return QVariant(entry.expression);
712 }
713 }
714
715 return QVariant();
716}
717
718void QQuickPropertyChanges::removeProperty(const QString &name)
719{
720 Q_D(QQuickPropertyChanges);
721
722 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
723 if (it->name == name) {
724 d->expressions.erase(pos: it);
725 state()->removeEntryFromRevertList(target: object(), name);
726 return;
727 }
728 }
729
730 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
731 if (it->first == name) {
732 d->properties.erase(pos: it);
733 state()->removeEntryFromRevertList(target: object(), name);
734 return;
735 }
736 }
737}
738
739QVariant QQuickPropertyChanges::value(const QString &name) const
740{
741 Q_D(const QQuickPropertyChanges);
742 typedef QPair<QString, QVariant> PropertyEntry;
743
744 for (const PropertyEntry &entry : d->properties) {
745 if (entry.first == name) {
746 return entry.second;
747 }
748 }
749
750 return QVariant();
751}
752
753QString QQuickPropertyChanges::expression(const QString &name) const
754{
755 Q_D(const QQuickPropertyChanges);
756 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
757
758 for (const ExpressionEntry &entry : d->expressions) {
759 if (entry.name == name) {
760 return entry.expression;
761 }
762 }
763
764 return QString();
765}
766
767void QQuickPropertyChanges::detachFromState()
768{
769 if (state())
770 state()->removeAllEntriesFromRevertList(target: object());
771}
772
773void QQuickPropertyChanges::attachToState()
774{
775 if (state())
776 state()->addEntriesToRevertList(actions: actions());
777}
778
779QT_END_NAMESPACE
780
781#include "moc_qquickpropertychanges_p.cpp"
782

source code of qtdeclarative/src/quick/util/qquickpropertychanges.cpp