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 | |
60 | QT_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 | |
140 | class QQuickReplaceSignalHandler : public QQuickStateActionEvent |
141 | { |
142 | public: |
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 | |
196 | class QQuickPropertyChangesPrivate : public QQuickStateOperationPrivate |
197 | { |
198 | Q_DECLARE_PUBLIC(QQuickPropertyChanges) |
199 | public: |
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 | |
240 | void 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 | |
257 | void 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 | |
270 | void 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 | |
350 | void 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 | |
356 | void 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 | |
365 | QQuickPropertyChanges::QQuickPropertyChanges() |
366 | : QQuickStateOperation(*(new QQuickPropertyChangesPrivate)) |
367 | { |
368 | } |
369 | |
370 | QQuickPropertyChanges::~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 | |
377 | QObject *QQuickPropertyChanges::object() const |
378 | { |
379 | Q_D(const QQuickPropertyChanges); |
380 | return d->object; |
381 | } |
382 | |
383 | void 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 | */ |
398 | bool QQuickPropertyChanges::restoreEntryValues() const |
399 | { |
400 | Q_D(const QQuickPropertyChanges); |
401 | return d->restore; |
402 | } |
403 | |
404 | void QQuickPropertyChanges::setRestoreEntryValues(bool v) |
405 | { |
406 | Q_D(QQuickPropertyChanges); |
407 | d->restore = v; |
408 | } |
409 | |
410 | QQmlProperty |
411 | QQuickPropertyChangesPrivate::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 | |
428 | QQuickPropertyChanges::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 | */ |
526 | bool QQuickPropertyChanges::isExplicit() const |
527 | { |
528 | Q_D(const QQuickPropertyChanges); |
529 | return d->isExplicit; |
530 | } |
531 | |
532 | void QQuickPropertyChanges::setIsExplicit(bool e) |
533 | { |
534 | Q_D(QQuickPropertyChanges); |
535 | d->isExplicit = e; |
536 | } |
537 | |
538 | bool 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 | |
552 | bool 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 | |
566 | bool QQuickPropertyChanges::containsProperty(const QString &name) const |
567 | { |
568 | return containsValue(name) || containsExpression(name); |
569 | } |
570 | |
571 | void 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 | |
616 | void 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 | |
697 | QVariant 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 | |
718 | void 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 | |
739 | QVariant 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 | |
753 | QString 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 | |
767 | void QQuickPropertyChanges::detachFromState() |
768 | { |
769 | if (state()) |
770 | state()->removeAllEntriesFromRevertList(target: object()); |
771 | } |
772 | |
773 | void QQuickPropertyChanges::attachToState() |
774 | { |
775 | if (state()) |
776 | state()->addEntriesToRevertList(actions: actions()); |
777 | } |
778 | |
779 | QT_END_NAMESPACE |
780 | |
781 | #include "moc_qquickpropertychanges_p.cpp" |
782 | |