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(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
218{
219 switch (binding->type()) {
220 case QV4::CompiledData::Binding::Type_Object:
221 error(object: compilationUnit->objectAt(index: binding->value.objectIndex),
222 description: QQuickPropertyChanges::tr(
223 s: "PropertyChanges does not support creating state-specific objects."));
224 break;
225 case QV4::CompiledData::Binding::Type_GroupProperty:
226 case QV4::CompiledData::Binding::Type_AttachedProperty: {
227 const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(index: binding->value.objectIndex);
228 const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
229 for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding)
230 verifyList(compilationUnit, binding: subBinding);
231 break;
232 }
233 default:
234 break;
235 }
236}
237
238void QQuickPropertyChangesPrivate::decode()
239{
240 if (decoded)
241 return;
242
243 for (const QV4::CompiledData::Binding *binding : std::as_const(t&: bindings))
244 decodeBinding(propertyPrefix: QString(), qmlUnit: compilationUnit, binding);
245
246 bindings.clear();
247
248 decoded = true;
249}
250
251void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
252{
253 Q_Q(QQuickPropertyChanges);
254
255 QString propertyName = propertyPrefix + compilationUnit->stringAt(index: binding->propertyNameIndex);
256
257 switch (binding->type()) {
258 case QV4::CompiledData::Binding::Type_GroupProperty:
259 case QV4::CompiledData::Binding::Type_AttachedProperty: {
260 QString pre = propertyName + QLatin1Char('.');
261 const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(index: binding->value.objectIndex);
262 const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
263 for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
264 decodeBinding(propertyPrefix: pre, compilationUnit, binding: subBinding);
265 }
266 return;
267 }
268 default:
269 break;
270 }
271
272 if (binding->isSignalHandler() || QQmlSignalNames::isHandlerName(signalName: propertyName)) {
273 QQmlProperty prop = property(propertyName);
274 if (prop.isSignalProperty()) {
275 QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler;
276 handler->property = prop;
277 handler->expression.adopt(
278 new QQmlBoundSignalExpression(
279 prop.object(), QQmlPropertyPrivate::get(p: prop)->signalIndex(),
280 QQmlContextData::get(context: qmlContext(q)), prop.object(),
281 compilationUnit->runtimeFunctions.at(i: binding->value.compiledScriptIndex)));
282 signalReplacements << handler;
283 return;
284 }
285 }
286
287 if (binding->type() == QV4::CompiledData::Binding::Type_Script
288 || binding->isTranslationBinding()) {
289 QUrl url = QUrl();
290 int line = -1;
291 int column = -1;
292
293 QQmlData *ddata = QQmlData::get(object: q);
294 if (ddata && ddata->outerContext && !ddata->outerContext->url().isEmpty()) {
295 url = ddata->outerContext->url();
296 line = ddata->lineNumber;
297 column = ddata->columnNumber;
298 }
299
300 QString expression;
301 QQmlBinding::Identifier id = QQmlBinding::Invalid;
302
303 if (!binding->isTranslationBinding()) {
304 expression = compilationUnit->bindingValueAsString(binding);
305 id = binding->value.compiledScriptIndex;
306 }
307 expressions << ExpressionChange(propertyName, binding, id, expression, url, line, column);
308 return;
309 }
310
311 QVariant var;
312 switch (binding->type()) {
313 case QV4::CompiledData::Binding::Type_Script:
314 case QV4::CompiledData::Binding::Type_Translation:
315 case QV4::CompiledData::Binding::Type_TranslationById:
316 Q_UNREACHABLE();
317 case QV4::CompiledData::Binding::Type_String:
318 var = compilationUnit->bindingValueAsString(binding);
319 break;
320 case QV4::CompiledData::Binding::Type_Number:
321 var = compilationUnit->bindingValueAsNumber(binding);
322 break;
323 case QV4::CompiledData::Binding::Type_Boolean:
324 var = binding->valueAsBoolean();
325 break;
326 case QV4::CompiledData::Binding::Type_Null:
327 var = QVariant::fromValue(value: nullptr);
328 break;
329 default:
330 break;
331 }
332
333 properties << qMakePair(value1&: propertyName, value2&: var);
334}
335
336void QQuickPropertyChangesParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
337{
338 for (int ii = 0; ii < props.size(); ++ii)
339 verifyList(compilationUnit, binding: props.at(i: ii));
340}
341
342void QQuickPropertyChangesParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
343{
344 QQuickPropertyChangesPrivate *p =
345 static_cast<QQuickPropertyChangesPrivate *>(QObjectPrivate::get(o: obj));
346 p->bindings = bindings;
347 p->compilationUnit = compilationUnit;
348 p->decoded = false;
349
350 QQmlData *data = QQmlData::get(object: obj);
351 Q_ASSERT(data && !data->wasDeleted(obj));
352 data->releaseDeferredData();
353}
354
355QQuickPropertyChanges::QQuickPropertyChanges()
356: QQuickStateOperation(*(new QQuickPropertyChangesPrivate))
357{
358}
359
360QQuickPropertyChanges::~QQuickPropertyChanges()
361{
362 Q_D(QQuickPropertyChanges);
363 for(int ii = 0; ii < d->signalReplacements.size(); ++ii)
364 delete d->signalReplacements.at(i: ii);
365}
366
367QObject *QQuickPropertyChanges::object() const
368{
369 Q_D(const QQuickPropertyChanges);
370 return d->object;
371}
372
373void QQuickPropertyChanges::setObject(QObject *o)
374{
375 Q_D(QQuickPropertyChanges);
376 if (o != d->object) {
377 d->object = o;
378 emit objectChanged();
379 }
380}
381
382/*!
383 \qmlproperty bool QtQuick::PropertyChanges::restoreEntryValues
384
385 This property holds whether the previous values should be restored when
386 leaving the state.
387
388 The default value is \c true. Setting this value to \c false creates a
389 temporary state that has permanent effects on property values.
390*/
391bool QQuickPropertyChanges::restoreEntryValues() const
392{
393 Q_D(const QQuickPropertyChanges);
394 return d->restore;
395}
396
397void QQuickPropertyChanges::setRestoreEntryValues(bool v)
398{
399 Q_D(QQuickPropertyChanges);
400 if (v != d->restore) {
401 d->restore = v;
402 emit restoreEntryValuesChanged();
403 }
404}
405
406QQmlProperty
407QQuickPropertyChangesPrivate::property(const QString &property)
408{
409 Q_Q(QQuickPropertyChanges);
410 QQmlData *ddata = QQmlData::get(object: q);
411 QQmlProperty prop = QQmlPropertyPrivate::create(
412 target: object, propertyName: property, context: ddata ? ddata->outerContext : QQmlRefPointer<QQmlContextData>(),
413 flags: QQmlPropertyPrivate::InitFlag::AllowId | QQmlPropertyPrivate::InitFlag::AllowSignal);
414 if (!prop.isValid()) {
415 qmlWarning(me: q) << QQuickPropertyChanges::tr(s: "Cannot assign to non-existent property \"%1\"").arg(a: property);
416 return QQmlProperty();
417 } else if (!(prop.type() & QQmlProperty::SignalProperty) && !prop.isWritable()) {
418 qmlWarning(me: q) << QQuickPropertyChanges::tr(s: "Cannot assign to read-only property \"%1\"").arg(a: property);
419 return QQmlProperty();
420 }
421 return prop;
422}
423
424QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions()
425{
426 Q_D(QQuickPropertyChanges);
427
428 d->decode();
429
430 ActionList list;
431
432 for (int ii = 0; ii < d->properties.size(); ++ii) {
433 QQmlProperty prop = d->property(property: d->properties.at(i: ii).first);
434
435 QQuickStateAction a(d->object, prop, d->properties.at(i: ii).first,
436 d->properties.at(i: ii).second);
437
438 if (a.property.isValid()) {
439 a.restore = restoreEntryValues();
440 list << a;
441 }
442 }
443
444 for (int ii = 0; ii < d->signalReplacements.size(); ++ii) {
445 QQuickReplaceSignalHandler *handler = d->signalReplacements.at(i: ii);
446
447 if (handler->property.isValid()) {
448 QQuickStateAction a;
449 a.event = handler;
450 list << a;
451 }
452 }
453
454 for (int ii = 0; ii < d->expressions.size(); ++ii) {
455
456 QQuickPropertyChangesPrivate::ExpressionChange e = d->expressions.at(i: ii);
457 const QString &property = e.name;
458 QQmlProperty prop = d->property(property);
459
460 if (prop.isValid()) {
461 QQuickStateAction a;
462 a.restore = restoreEntryValues();
463 a.property = prop;
464 a.fromValue = a.property.read();
465 a.specifiedObject = d->object;
466 a.specifiedProperty = property;
467
468 QQmlRefPointer<QQmlContextData> context = QQmlContextData::get(context: qmlContext(this));
469 QV4::Scope scope(qmlEngine(this)->handle());
470 QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, QV4::QmlContext::create(parent: scope.engine->rootContext(), context, scopeObject: object()));
471
472 if (d->isExplicit) {
473 // in this case, we don't want to assign a binding, per se,
474 // so we evaluate the expression and assign the result.
475 // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
476 // so that we can avoid creating then destroying the binding in this case.
477 std::unique_ptr<QQmlBinding> newBinding = nullptr;
478 if (e.binding && e.binding->isTranslationBinding()) {
479 newBinding.reset(p: QQmlBinding::createTranslationBinding(unit: d->compilationUnit, binding: e.binding, obj: object(), ctxt: context));
480 } else if (e.id != QQmlBinding::Invalid) {
481 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));
482 } else {
483 newBinding.reset(p: QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, e.expression, object(), context, url: e.url.toString(), lineNumber: e.line));
484 }
485 a.toValue = newBinding->evaluate();
486 } else {
487 QQmlAnyBinding newBinding = nullptr;
488 if (e.binding && e.binding->isTranslationBinding()) {
489 newBinding = QQmlAnyBinding::createTranslationBinding(prop, compilationUnit: d->compilationUnit, translationBinding: e.binding, scopeObject: object(), context);
490 } else if (e.id != QQmlBinding::Invalid) {
491 newBinding = QQmlAnyBinding::createFromFunction(prop,
492 function: d->compilationUnit->runtimeFunctions.at(i: e.id),
493 obj: object(), ctxt: context, scope: qmlCtxt);
494 } else {
495 newBinding = QQmlAnyBinding::createFromCodeString(prop, code: e.expression, obj: object(), ctxt: context, url: e.url.toString(), lineNumber: e.line);
496 }
497
498 a.toBinding = newBinding;
499 a.deletableToBinding = true;
500 }
501
502 list << a;
503 }
504 }
505
506 return list;
507}
508
509/*!
510 \qmlproperty bool QtQuick::PropertyChanges::explicit
511
512 If explicit is set to true, any potential bindings will be interpreted as
513 once-off assignments that occur when the state is entered.
514
515 In the following example, the addition of explicit prevents \c myItem.width from
516 being bound to \c parent.width. Instead, it is assigned the value of \c parent.width
517 at the time of the state change.
518 \qml
519 PropertyChanges {
520 target: myItem
521 explicit: true
522 width: parent.width
523 }
524 \endqml
525
526 By default, explicit is false.
527*/
528bool QQuickPropertyChanges::isExplicit() const
529{
530 Q_D(const QQuickPropertyChanges);
531 return d->isExplicit;
532}
533
534void QQuickPropertyChanges::setIsExplicit(bool e)
535{
536 Q_D(QQuickPropertyChanges);
537 if (e != d->isExplicit) {
538 d->isExplicit = e;
539 emit isExplicitChanged();
540 }
541}
542
543bool QQuickPropertyChanges::containsValue(const QString &name) const
544{
545 Q_D(const QQuickPropertyChanges);
546 typedef QPair<QString, QVariant> PropertyEntry;
547
548 for (const PropertyEntry &entry : d->properties) {
549 if (entry.first == name) {
550 return true;
551 }
552 }
553
554 return false;
555}
556
557bool QQuickPropertyChanges::containsExpression(const QString &name) const
558{
559 Q_D(const QQuickPropertyChanges);
560 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
561
562 for (const ExpressionEntry &entry : d->expressions) {
563 if (entry.name == name) {
564 return true;
565 }
566 }
567
568 return false;
569}
570
571bool QQuickPropertyChanges::containsProperty(const QString &name) const
572{
573 return containsValue(name) || containsExpression(name);
574}
575
576void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &value)
577{
578 Q_D(QQuickPropertyChanges);
579 typedef QPair<QString, QVariant> PropertyEntry;
580
581 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
582 if (it->name == name) {
583 d->expressions.erase(pos: it);
584 if (state() && state()->isStateActive()) {
585 QQmlPropertyPrivate::removeBinding(that: d->property(property: name));
586 d->property(property: name).write(value);
587 }
588
589 d->properties.append(t: PropertyEntry(name, value));
590 return;
591 }
592 }
593
594 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
595 if (it->first == name) {
596 it->second = value;
597 if (state() && state()->isStateActive())
598 d->property(property: name).write(value);
599 return;
600 }
601 }
602
603 QQuickStateAction action;
604 action.restore = restoreEntryValues();
605 action.property = d->property(property: name);
606 action.fromValue = action.property.read();
607 action.specifiedObject = object();
608 action.specifiedProperty = name;
609 action.toValue = value;
610
611 d->properties.append(t: PropertyEntry(name, value));
612 if (state() && state()->isStateActive()) {
613 state()->addEntryToRevertList(action);
614 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(that: action.property);
615 if (oldBinding)
616 oldBinding->setEnabled(e: false, f: QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
617 d->property(property: name).write(value);
618 }
619}
620
621void QQuickPropertyChanges::changeExpression(const QString &name, const QString &expression)
622{
623 Q_D(QQuickPropertyChanges);
624 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
625
626 bool hadValue = false;
627
628 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
629 if (it->first == name) {
630 d->properties.erase(pos: it);
631 hadValue = true;
632 break;
633 }
634 }
635
636 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
637 if (it->name == name) {
638 it->expression = expression;
639 if (state() && state()->isStateActive()) {
640 auto prop = d->property(property: name);
641 auto context = QQmlContextData::get(context: qmlContext(this));
642 QString url;
643 int lineNumber = 0;
644 QQmlAnyBinding::createFromCodeString(prop, code: expression, obj: object(), ctxt: context, url, lineNumber).installOn(target: prop);
645 }
646 return;
647 }
648 }
649
650 // adding a new expression.
651 d->expressions.append(t: ExpressionEntry(name, nullptr, QQmlBinding::Invalid, expression, QUrl(), -1, -1));
652
653 const QString url;
654 const quint16 lineNumber = 0;
655 if (state() && state()->isStateActive()) {
656 if (hadValue) {
657 auto prop = d->property(property: name);
658 QQmlAnyBinding oldBinding = QQmlAnyBinding::takeFrom(prop);
659 if (oldBinding)
660 state()->changeBindingInRevertList(target: object(), name, binding: oldBinding);
661
662 QQmlAnyBinding::createFromCodeString(prop, code: expression, obj: object(), ctxt: QQmlContextData::get(context: qmlContext(this)), url, lineNumber).installOn(target: prop);
663 } else {
664 QQuickStateAction action;
665 action.restore = restoreEntryValues();
666 action.property = d->property(property: name);
667 action.fromValue = action.property.read();
668 action.specifiedObject = object();
669 action.specifiedProperty = name;
670
671 QQmlAnyBinding newBinding;
672 if (d->isExplicit) {
673 newBinding = QQmlBinding::create(
674 &QQmlPropertyPrivate::get(p: action.property)->core, expression,
675 object(), QQmlContextData::get(context: qmlContext(this)));
676 } else {
677 const auto prop = action.property;
678 const auto context = QQmlContextData::get(context: qmlContext(this));
679 newBinding = QQmlAnyBinding::createFromCodeString(prop, code: expression, obj: object(), ctxt: context, url, lineNumber);
680 }
681 if (d->isExplicit) {
682 // don't assign the binding, merely evaluate the expression.
683 // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
684 // so that we can avoid creating then destroying the binding in this case.
685 action.toValue = static_cast<QQmlBinding *>(newBinding.asAbstractBinding())->evaluate();
686 } else {
687 // TODO: replace binding would be more efficient for new-style properties
688 QQmlAnyBinding::removeBindingFrom(prop&: action.property);
689 newBinding.installOn(target: action.property);
690 action.toBinding = newBinding;
691 action.deletableToBinding = true;
692 state()->addEntryToRevertList(action);
693 }
694 }
695 }
696 // what about the signal handler?
697}
698
699QVariant QQuickPropertyChanges::property(const QString &name) const
700{
701 Q_D(const QQuickPropertyChanges);
702 typedef QPair<QString, QVariant> PropertyEntry;
703 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
704
705 for (const PropertyEntry &entry : d->properties) {
706 if (entry.first == name) {
707 return entry.second;
708 }
709 }
710
711 for (const ExpressionEntry &entry : d->expressions) {
712 if (entry.name == name) {
713 return QVariant(entry.expression);
714 }
715 }
716
717 return QVariant();
718}
719
720void QQuickPropertyChanges::removeProperty(const QString &name)
721{
722 Q_D(QQuickPropertyChanges);
723
724 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
725 if (it->name == name) {
726 d->expressions.erase(pos: it);
727 state()->removeEntryFromRevertList(target: object(), name);
728 return;
729 }
730 }
731
732 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
733 if (it->first == name) {
734 d->properties.erase(pos: it);
735 state()->removeEntryFromRevertList(target: object(), name);
736 return;
737 }
738 }
739}
740
741QVariant QQuickPropertyChanges::value(const QString &name) const
742{
743 Q_D(const QQuickPropertyChanges);
744 typedef QPair<QString, QVariant> PropertyEntry;
745
746 for (const PropertyEntry &entry : d->properties) {
747 if (entry.first == name) {
748 return entry.second;
749 }
750 }
751
752 return QVariant();
753}
754
755QString QQuickPropertyChanges::expression(const QString &name) const
756{
757 Q_D(const QQuickPropertyChanges);
758 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
759
760 for (const ExpressionEntry &entry : d->expressions) {
761 if (entry.name == name) {
762 return entry.expression;
763 }
764 }
765
766 return QString();
767}
768
769void QQuickPropertyChanges::detachFromState()
770{
771 if (state())
772 state()->removeAllEntriesFromRevertList(target: object());
773}
774
775void QQuickPropertyChanges::attachToState()
776{
777 if (state())
778 state()->addEntriesToRevertList(actions: actions());
779}
780
781QT_END_NAMESPACE
782
783#include "moc_qquickpropertychanges_p.cpp"
784

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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