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

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