1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickpropertychanges_p.h"
41
42#include <private/qqmlopenmetaobject_p.h>
43#include <private/qqmlengine_p.h>
44
45#include <qqmlinfo.h>
46#include <private/qqmlcustomparser_p.h>
47#include <qqmlexpression.h>
48#include <private/qqmlbinding_p.h>
49#include <qqmlcontext.h>
50#include <private/qqmlproperty_p.h>
51#include <private/qqmlcontext_p.h>
52#include <private/qquickstate_p_p.h>
53#include <private/qqmlboundsignal_p.h>
54#include <private/qv4qmlcontext_p.h>
55
56#include <QtCore/qdebug.h>
57
58#include <private/qobject_p.h>
59
60QT_BEGIN_NAMESPACE
61
62/*!
63 \qmltype PropertyChanges
64 \inqmlmodule QtQuick
65 \ingroup qtquick-states
66 \brief Describes new property bindings or values for a state.
67
68 PropertyChanges is used to define the property values or bindings in a
69 \l State. This enables an item's property values to be changed when it
70 \l {Qt Quick States}{changes between states}.
71
72 To create a PropertyChanges object, specify the \l target item whose
73 properties are to be modified, and define the new property values or
74 bindings. For example:
75
76 \snippet qml/propertychanges.qml import
77 \codeline
78 \snippet qml/propertychanges.qml 0
79
80 When the mouse is pressed, the \l Rectangle changes to the \e resized
81 state. In this state, the PropertyChanges object sets the rectangle's
82 color to blue and the \c height value to that of \c container.height.
83
84 Note this automatically binds \c rect.height to \c container.height
85 in the \e resized state. If a property binding should not be
86 established, and the height should just be set to the value of
87 \c container.height at the time of the state change, set the \l explicit
88 property to \c true.
89
90 A PropertyChanges object can also override the default signal handler
91 for an object to implement a signal handler specific to the new state:
92
93 \qml
94 PropertyChanges {
95 target: myMouseArea
96 onClicked: doSomethingDifferent()
97 }
98 \endqml
99
100 \note PropertyChanges can be used to change anchor margins, but not other anchor
101 values; use AnchorChanges for this instead. Similarly, to change an \l Item's
102 \l {Item::}{parent} value, use ParentChange instead.
103
104
105 \section2 Resetting Property Values
106
107 The \c undefined value can be used to reset the property value for a state.
108 In the following example, when \c myText changes to the \e widerText
109 state, its \c width property is reset, giving the text its natural width
110 and displaying the whole string on a single line.
111
112 \snippet qml/propertychanges.qml reset
113
114
115 \section2 Immediate Property Changes in Transitions
116
117 When \l{Animation and Transitions in Qt Quick}{Transitions} are used to animate
118 state changes, they animate properties from their values in the current
119 state to those defined in the new state (as defined by PropertyChanges
120 objects). However, it is sometimes desirable to set a property value
121 \e immediately during a \l Transition, without animation; in these cases,
122 the PropertyAction type can be used to force an immediate property
123 change.
124
125 See the PropertyAction documentation for more details.
126
127 \note The \l{Item::}{visible} and \l{Item::}{enabled} properties of \l Item do not behave
128 exactly the same as other properties in PropertyChanges. Since these properties can be
129 changed implicitly through their parent's state, they should be set explicitly in all PropertyChanges.
130 An item will still not be enabled/visible if one of its parents is not enabled or visible.
131
132 \sa {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States}, {Qt QML}
133*/
134
135/*!
136 \qmlproperty Object QtQuick::PropertyChanges::target
137 This property holds the object which contains the properties to be changed.
138*/
139
140class QQuickReplaceSignalHandler : public QQuickStateActionEvent
141{
142public:
143 QQuickReplaceSignalHandler() {}
144 ~QQuickReplaceSignalHandler() {}
145
146 EventType type() const override { return SignalHandler; }
147
148 QQmlProperty property;
149 QQmlBoundSignalExpressionPointer expression;
150 QQmlBoundSignalExpressionPointer reverseExpression;
151 QQmlBoundSignalExpressionPointer rewindExpression;
152
153 void execute() override {
154 QQmlPropertyPrivate::setSignalExpression(that: property, expression);
155 }
156
157 bool isReversable() override { return true; }
158 void reverse() override {
159 QQmlPropertyPrivate::setSignalExpression(that: property, reverseExpression);
160 }
161
162 void saveOriginals() override {
163 saveCurrentValues();
164 reverseExpression = rewindExpression;
165 }
166
167 bool needsCopy() override { return true; }
168 void copyOriginals(QQuickStateActionEvent *other) override
169 {
170 QQuickReplaceSignalHandler *rsh = static_cast<QQuickReplaceSignalHandler*>(other);
171 saveCurrentValues();
172 if (rsh == this)
173 return;
174 reverseExpression = rsh->reverseExpression;
175 }
176
177 void rewind() override {
178 QQmlPropertyPrivate::setSignalExpression(that: property, rewindExpression);
179 }
180 void saveCurrentValues() override {
181 rewindExpression = QQmlPropertyPrivate::signalExpression(that: property);
182 }
183
184 bool mayOverride(QQuickStateActionEvent *other) override {
185 if (other == this)
186 return true;
187 if (other->type() != type())
188 return false;
189 if (static_cast<QQuickReplaceSignalHandler*>(other)->property == property)
190 return true;
191 return false;
192 }
193};
194
195
196class QQuickPropertyChangesPrivate : public QQuickStateOperationPrivate
197{
198 Q_DECLARE_PUBLIC(QQuickPropertyChanges)
199public:
200 QQuickPropertyChangesPrivate() : decoded(true), restore(true),
201 isExplicit(false) {}
202
203 QPointer<QObject> object;
204 QList<const QV4::CompiledData::Binding *> bindings;
205 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
206
207 bool decoded : 1;
208 bool restore : 1;
209 bool isExplicit : 1;
210
211 void decode();
212 void decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlUnit, const QV4::CompiledData::Binding *binding);
213
214 class ExpressionChange {
215 public:
216 ExpressionChange(const QString &_name,
217 const QV4::CompiledData::Binding *_binding,
218 QQmlBinding::Identifier _id,
219 const QString& _expr,
220 const QUrl &_url,
221 int _line,
222 int _column)
223 : name(_name), binding(_binding), id(_id), expression(_expr), url(_url), line(_line), column(_column) {}
224 QString name;
225 const QV4::CompiledData::Binding *binding;
226 QQmlBinding::Identifier id;
227 QString expression;
228 QUrl url;
229 int line;
230 int column;
231 };
232
233 QList<QPair<QString, QVariant> > properties;
234 QList<ExpressionChange> expressions;
235 QList<QQuickReplaceSignalHandler*> signalReplacements;
236
237 QQmlProperty property(const QString &);
238};
239
240void QQuickPropertyChangesParser::verifyList(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
241{
242 if (binding->type == QV4::CompiledData::Binding::Type_Object) {
243 error(object: compilationUnit->objectAt(index: binding->value.objectIndex), description: QQuickPropertyChanges::tr(s: "PropertyChanges does not support creating state-specific objects."));
244 return;
245 }
246
247 if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
248 || binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
249 const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(index: binding->value.objectIndex);
250 const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
251 for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
252 verifyList(compilationUnit, binding: subBinding);
253 }
254 }
255}
256
257void QQuickPropertyChangesPrivate::decode()
258{
259 if (decoded)
260 return;
261
262 for (const QV4::CompiledData::Binding *binding : qAsConst(t&: bindings))
263 decodeBinding(propertyPrefix: QString(), qmlUnit: compilationUnit, binding);
264
265 bindings.clear();
266
267 decoded = true;
268}
269
270void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
271{
272 Q_Q(QQuickPropertyChanges);
273
274 QString propertyName = propertyPrefix + compilationUnit->stringAt(index: binding->propertyNameIndex);
275
276 if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
277 || binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
278 QString pre = propertyName + QLatin1Char('.');
279 const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(index: binding->value.objectIndex);
280 const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
281 for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
282 decodeBinding(propertyPrefix: pre, compilationUnit, binding: subBinding);
283 }
284 return;
285 }
286
287 if (propertyName.count() >= 3 &&
288 propertyName.at(i: 0) == QLatin1Char('o') &&
289 propertyName.at(i: 1) == QLatin1Char('n') &&
290 propertyName.at(i: 2).isUpper()) {
291 QQmlProperty prop = property(propertyName);
292 if (prop.isSignalProperty()) {
293 QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler;
294 handler->property = prop;
295 handler->expression.take(new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(p: prop)->signalIndex(),
296 QQmlContextData::get(context: qmlContext(q)), object, compilationUnit->runtimeFunctions.at(i: binding->value.compiledScriptIndex)));
297 signalReplacements << handler;
298 return;
299 }
300 }
301
302 if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
303 QUrl url = QUrl();
304 int line = -1;
305 int column = -1;
306
307 QQmlData *ddata = QQmlData::get(object: q);
308 if (ddata && ddata->outerContext && !ddata->outerContext->url().isEmpty()) {
309 url = ddata->outerContext->url();
310 line = ddata->lineNumber;
311 column = ddata->columnNumber;
312 }
313
314 QString expression;
315 QQmlBinding::Identifier id = QQmlBinding::Invalid;
316
317 if (!binding->isTranslationBinding()) {
318 expression = compilationUnit->bindingValueAsString(binding);
319 id = binding->value.compiledScriptIndex;
320 }
321 expressions << ExpressionChange(propertyName, binding, id, expression, url, line, column);
322 return;
323 }
324
325 QVariant var;
326 switch (binding->type) {
327 case QV4::CompiledData::Binding::Type_Script:
328 case QV4::CompiledData::Binding::Type_Translation:
329 case QV4::CompiledData::Binding::Type_TranslationById:
330 Q_UNREACHABLE();
331 case QV4::CompiledData::Binding::Type_String:
332 var = compilationUnit->bindingValueAsString(binding);
333 break;
334 case QV4::CompiledData::Binding::Type_Number:
335 var = compilationUnit->bindingValueAsNumber(binding);
336 break;
337 case QV4::CompiledData::Binding::Type_Boolean:
338 var = binding->valueAsBoolean();
339 break;
340 case QV4::CompiledData::Binding::Type_Null:
341 var = QVariant::fromValue(value: nullptr);
342 break;
343 default:
344 break;
345 }
346
347 properties << qMakePair(x: propertyName, y: var);
348}
349
350void QQuickPropertyChangesParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
351{
352 for (int ii = 0; ii < props.count(); ++ii)
353 verifyList(compilationUnit, binding: props.at(i: ii));
354}
355
356void QQuickPropertyChangesParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
357{
358 QQuickPropertyChangesPrivate *p =
359 static_cast<QQuickPropertyChangesPrivate *>(QObjectPrivate::get(o: obj));
360 p->bindings = bindings;
361 p->compilationUnit = compilationUnit;
362 p->decoded = false;
363}
364
365QQuickPropertyChanges::QQuickPropertyChanges()
366: QQuickStateOperation(*(new QQuickPropertyChangesPrivate))
367{
368}
369
370QQuickPropertyChanges::~QQuickPropertyChanges()
371{
372 Q_D(QQuickPropertyChanges);
373 for(int ii = 0; ii < d->signalReplacements.count(); ++ii)
374 delete d->signalReplacements.at(i: ii);
375}
376
377QObject *QQuickPropertyChanges::object() const
378{
379 Q_D(const QQuickPropertyChanges);
380 return d->object;
381}
382
383void QQuickPropertyChanges::setObject(QObject *o)
384{
385 Q_D(QQuickPropertyChanges);
386 d->object = o;
387}
388
389/*!
390 \qmlproperty bool QtQuick::PropertyChanges::restoreEntryValues
391
392 This property holds whether the previous values should be restored when
393 leaving the state.
394
395 The default value is \c true. Setting this value to \c false creates a
396 temporary state that has permanent effects on property values.
397*/
398bool QQuickPropertyChanges::restoreEntryValues() const
399{
400 Q_D(const QQuickPropertyChanges);
401 return d->restore;
402}
403
404void QQuickPropertyChanges::setRestoreEntryValues(bool v)
405{
406 Q_D(QQuickPropertyChanges);
407 d->restore = v;
408}
409
410QQmlProperty
411QQuickPropertyChangesPrivate::property(const QString &property)
412{
413 Q_Q(QQuickPropertyChanges);
414 QQmlContextData *context = nullptr;
415 if (QQmlData *ddata = QQmlData::get(object: q))
416 context = ddata->outerContext;
417 QQmlProperty prop = QQmlPropertyPrivate::create(target: object, propertyName: property, context);
418 if (!prop.isValid()) {
419 qmlWarning(me: q) << QQuickPropertyChanges::tr(s: "Cannot assign to non-existent property \"%1\"").arg(a: property);
420 return QQmlProperty();
421 } else if (!(prop.type() & QQmlProperty::SignalProperty) && !prop.isWritable()) {
422 qmlWarning(me: q) << QQuickPropertyChanges::tr(s: "Cannot assign to read-only property \"%1\"").arg(a: property);
423 return QQmlProperty();
424 }
425 return prop;
426}
427
428QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions()
429{
430 Q_D(QQuickPropertyChanges);
431
432 d->decode();
433
434 ActionList list;
435
436 for (int ii = 0; ii < d->properties.count(); ++ii) {
437 QQmlProperty prop = d->property(property: d->properties.at(i: ii).first);
438
439 QQuickStateAction a(d->object, prop, d->properties.at(i: ii).first,
440 d->properties.at(i: ii).second);
441
442 if (a.property.isValid()) {
443 a.restore = restoreEntryValues();
444 list << a;
445 }
446 }
447
448 for (int ii = 0; ii < d->signalReplacements.count(); ++ii) {
449 QQuickReplaceSignalHandler *handler = d->signalReplacements.at(i: ii);
450
451 if (handler->property.isValid()) {
452 QQuickStateAction a;
453 a.event = handler;
454 list << a;
455 }
456 }
457
458 for (int ii = 0; ii < d->expressions.count(); ++ii) {
459
460 QQuickPropertyChangesPrivate::ExpressionChange e = d->expressions.at(i: ii);
461 const QString &property = e.name;
462 QQmlProperty prop = d->property(property);
463
464 if (prop.isValid()) {
465 QQuickStateAction a;
466 a.restore = restoreEntryValues();
467 a.property = prop;
468 a.fromValue = a.property.read();
469 a.specifiedObject = d->object;
470 a.specifiedProperty = property;
471
472 QQmlContextData *context = QQmlContextData::get(context: qmlContext(this));
473
474 QQmlBinding *newBinding = nullptr;
475 if (e.binding && e.binding->isTranslationBinding()) {
476 newBinding = QQmlBinding::createTranslationBinding(unit: d->compilationUnit, binding: e.binding, obj: object(), ctxt: context);
477 } else if (e.id != QQmlBinding::Invalid) {
478 QV4::Scope scope(qmlEngine(this)->handle());
479 QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(parent: scope.engine->rootContext(), context, scopeObject: object()));
480 newBinding = QQmlBinding::create(property: &QQmlPropertyPrivate::get(p: prop)->core,
481 function: d->compilationUnit->runtimeFunctions.at(i: e.id), obj: object(), ctxt: context, scope: qmlContext);
482 }
483 if (!newBinding)
484 newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core,
485 e.expression, object(), context, url: e.url.toString(), lineNumber: e.line);
486
487 if (d->isExplicit) {
488 // in this case, we don't want to assign a binding, per se,
489 // so we evaluate the expression and assign the result.
490 // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
491 // so that we can avoid creating then destroying the binding in this case.
492 a.toValue = newBinding->evaluate();
493 delete newBinding;
494 } else {
495 newBinding->setTarget(prop);
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 d->isExplicit = e;
536}
537
538bool QQuickPropertyChanges::containsValue(const QString &name) const
539{
540 Q_D(const QQuickPropertyChanges);
541 typedef QPair<QString, QVariant> PropertyEntry;
542
543 for (const PropertyEntry &entry : d->properties) {
544 if (entry.first == name) {
545 return true;
546 }
547 }
548
549 return false;
550}
551
552bool QQuickPropertyChanges::containsExpression(const QString &name) const
553{
554 Q_D(const QQuickPropertyChanges);
555 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
556
557 for (const ExpressionEntry &entry : d->expressions) {
558 if (entry.name == name) {
559 return true;
560 }
561 }
562
563 return false;
564}
565
566bool QQuickPropertyChanges::containsProperty(const QString &name) const
567{
568 return containsValue(name) || containsExpression(name);
569}
570
571void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &value)
572{
573 Q_D(QQuickPropertyChanges);
574 typedef QPair<QString, QVariant> PropertyEntry;
575
576 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
577 if (it->name == name) {
578 d->expressions.erase(pos: it);
579 if (state() && state()->isStateActive()) {
580 QQmlPropertyPrivate::removeBinding(that: d->property(property: name));
581 d->property(property: name).write(value);
582 }
583
584 d->properties.append(t: PropertyEntry(name, value));
585 return;
586 }
587 }
588
589 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
590 if (it->first == name) {
591 it->second = value;
592 if (state() && state()->isStateActive())
593 d->property(property: name).write(value);
594 return;
595 }
596 }
597
598 QQuickStateAction action;
599 action.restore = restoreEntryValues();
600 action.property = d->property(property: name);
601 action.fromValue = action.property.read();
602 action.specifiedObject = object();
603 action.specifiedProperty = name;
604 action.toValue = value;
605
606 d->properties.append(t: PropertyEntry(name, value));
607 if (state() && state()->isStateActive()) {
608 state()->addEntryToRevertList(action);
609 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(that: action.property);
610 if (oldBinding)
611 oldBinding->setEnabled(e: false, f: QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
612 d->property(property: name).write(value);
613 }
614}
615
616void QQuickPropertyChanges::changeExpression(const QString &name, const QString &expression)
617{
618 Q_D(QQuickPropertyChanges);
619 typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
620
621 bool hadValue = false;
622
623 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
624 if (it->first == name) {
625 d->properties.erase(pos: it);
626 hadValue = true;
627 break;
628 }
629 }
630
631 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
632 if (it->name == name) {
633 it->expression = expression;
634 if (state() && state()->isStateActive()) {
635 auto prop = d->property(property: name);
636 QQmlBinding *newBinding = QQmlBinding::create(
637 &QQmlPropertyPrivate::get(p: prop)->core, expression, object(),
638 QQmlContextData::get(context: qmlContext(this)));
639 newBinding->setTarget(prop);
640 QQmlPropertyPrivate::setBinding(binding: newBinding, flags: QQmlPropertyPrivate::None, writeFlags: QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
641 }
642 return;
643 }
644 }
645
646 // adding a new expression.
647 d->expressions.append(t: ExpressionEntry(name, nullptr, QQmlBinding::Invalid, expression, QUrl(), -1, -1));
648
649 if (state() && state()->isStateActive()) {
650 if (hadValue) {
651 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(that: d->property(property: name));
652 if (oldBinding) {
653 oldBinding->setEnabled(e: false, f: QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
654 state()->changeBindingInRevertList(target: object(), name, binding: oldBinding);
655 }
656
657 auto prop = d->property(property: name);
658 QQmlBinding *newBinding = QQmlBinding::create(
659 &QQmlPropertyPrivate::get(p: prop)->core, expression, object(),
660 QQmlContextData::get(context: qmlContext(this)));
661 newBinding->setTarget(prop);
662 QQmlPropertyPrivate::setBinding(binding: newBinding, flags: QQmlPropertyPrivate::None, writeFlags: QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
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 QQmlBinding *newBinding = QQmlBinding::create(
672 &QQmlPropertyPrivate::get(p: action.property)->core, expression,
673 object(), QQmlContextData::get(context: qmlContext(this)));
674 if (d->isExplicit) {
675 // don't assign the binding, merely evaluate the expression.
676 // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
677 // so that we can avoid creating then destroying the binding in this case.
678 action.toValue = newBinding->evaluate();
679 delete newBinding;
680 } else {
681 newBinding->setTarget(action.property);
682 action.toBinding = newBinding;
683 action.deletableToBinding = true;
684
685 state()->addEntryToRevertList(action);
686 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(that: action.property);
687 if (oldBinding)
688 oldBinding->setEnabled(e: false, f: QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
689
690 QQmlPropertyPrivate::setBinding(binding: newBinding, flags: QQmlPropertyPrivate::None, writeFlags: QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
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