Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qquickpropertychanges.cpp
Go to the documentation of this file.
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
5
6#include <private/qqmlopenmetaobject_p.h>
7#include <private/qqmlengine_p.h>
8
9#include <qqmlinfo.h>
10#include <private/qqmlcustomparser_p.h>
11#include <qqmlexpression.h>
12#include <private/qqmlbinding_p.h>
13#include <qqmlcontext.h>
14#include <private/qqmlproperty_p.h>
15#include <private/qqmlcontext_p.h>
16#include <private/qquickstate_p_p.h>
17#include <private/qqmlboundsignal_p.h>
18#include <private/qv4qmlcontext_p.h>
19#include <private/qqmlpropertybinding_p.h>
20#include <private/qqmlirbuilder_p.h>
21
22#include <QtCore/qdebug.h>
23
24#include <private/qobject_p.h>
25
27
116{
117public:
120
121 EventType type() const override { return SignalHandler; }
122
127
128 void execute() override {
130 }
131
132 bool isReversable() override { return true; }
133 void reverse() override {
135 }
136
137 void saveOriginals() override {
140 }
141
142 bool needsCopy() override { return true; }
144 {
147 if (rsh == this)
148 return;
150 }
151
152 void rewind() override {
154 }
155 void saveCurrentValues() override {
157 }
158
160 if (other == this)
161 return true;
162 if (other->type() != type())
163 return false;
164 if (static_cast<QQuickReplaceSignalHandler*>(other)->property == property)
165 return true;
166 return false;
167 }
168};
169
170
172{
173 Q_DECLARE_PUBLIC(QQuickPropertyChanges)
174public:
176 isExplicit(false) {}
177
181
182 bool decoded : 1;
183 bool restore : 1;
184 bool isExplicit : 1;
185
186 void decode();
187 void decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlUnit, const QV4::CompiledData::Binding *binding);
188
190 public:
192 const QV4::CompiledData::Binding *_binding,
194 const QString& _expr,
195 const QUrl &_url,
196 int _line,
197 int _column)
198 : name(_name), binding(_binding), id(_id), expression(_expr), url(_url), line(_line), column(_column) {}
204 int line;
206 };
207
211
213};
214
216{
217 switch (binding->type()) {
219 error(compilationUnit->objectAt(binding->value.objectIndex),
220 QQuickPropertyChanges::tr(
221 "PropertyChanges does not support creating state-specific objects."));
222 break;
225 const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex);
226 const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
227 for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding)
228 verifyList(compilationUnit, subBinding);
229 break;
230 }
231 default:
232 break;
233 }
234}
235
237{
238 if (decoded)
239 return;
240
241 for (const QV4::CompiledData::Binding *binding : std::as_const(bindings))
243
244 bindings.clear();
245
246 decoded = true;
247}
248
250{
252
253 QString propertyName = propertyPrefix + compilationUnit->stringAt(binding->propertyNameIndex);
254
255 switch (binding->type()) {
258 QString pre = propertyName + QLatin1Char('.');
260 const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
261 for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
262 decodeBinding(pre, compilationUnit, subBinding);
263 }
264 return;
265 }
266 default:
267 break;
268 }
269
270 if (binding->isSignalHandler() || QmlIR::IRBuilder::isSignalPropertyName(propertyName)) {
271 QQmlProperty prop = property(propertyName);
272 if (prop.isSignalProperty()) {
274 handler->property = prop;
275 handler->expression.adopt(
280 signalReplacements << handler;
281 return;
282 }
283 }
284
286 || binding->isTranslationBinding()) {
287 QUrl url = QUrl();
288 int line = -1;
289 int column = -1;
290
291 QQmlData *ddata = QQmlData::get(q);
292 if (ddata && ddata->outerContext && !ddata->outerContext->url().isEmpty()) {
293 url = ddata->outerContext->url();
294 line = ddata->lineNumber;
295 column = ddata->columnNumber;
296 }
297
298 QString expression;
300
301 if (!binding->isTranslationBinding()) {
302 expression = compilationUnit->bindingValueAsString(binding);
303 id = binding->value.compiledScriptIndex;
304 }
305 expressions << ExpressionChange(propertyName, binding, id, expression, url, line, column);
306 return;
307 }
308
310 switch (binding->type()) {
314 Q_UNREACHABLE();
317 break;
320 break;
322 var = binding->valueAsBoolean();
323 break;
325 var = QVariant::fromValue(nullptr);
326 break;
327 default:
328 break;
329 }
330
331 properties << qMakePair(propertyName, var);
332}
333
335{
336 for (int ii = 0; ii < props.size(); ++ii)
337 verifyList(compilationUnit, props.at(ii));
338}
339
341{
344 p->bindings = bindings;
345 p->compilationUnit = compilationUnit;
346 p->decoded = false;
347
349 Q_ASSERT(data && !data->wasDeleted(obj));
350 data->releaseDeferredData();
351}
352
355{
356}
357
359{
361 for(int ii = 0; ii < d->signalReplacements.size(); ++ii)
362 delete d->signalReplacements.at(ii);
363}
364
366{
367 Q_D(const QQuickPropertyChanges);
368 return d->object;
369}
370
372{
374 if (o != d->object) {
375 d->object = o;
377 }
378}
379
390{
391 Q_D(const QQuickPropertyChanges);
392 return d->restore;
393}
394
396{
398 if (v != d->restore) {
399 d->restore = v;
401 }
402}
403
406{
408 QQmlData *ddata = QQmlData::get(q);
410 object, property, ddata ? ddata->outerContext : QQmlRefPointer<QQmlContextData>(),
412 if (!prop.isValid()) {
413 qmlWarning(q) << QQuickPropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(property);
414 return QQmlProperty();
415 } else if (!(prop.type() & QQmlProperty::SignalProperty) && !prop.isWritable()) {
416 qmlWarning(q) << QQuickPropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(property);
417 return QQmlProperty();
418 }
419 return prop;
420}
421
423{
425
426 d->decode();
427
429
430 for (int ii = 0; ii < d->properties.size(); ++ii) {
431 QQmlProperty prop = d->property(d->properties.at(ii).first);
432
433 QQuickStateAction a(d->object, prop, d->properties.at(ii).first,
434 d->properties.at(ii).second);
435
436 if (a.property.isValid()) {
437 a.restore = restoreEntryValues();
438 list << a;
439 }
440 }
441
442 for (int ii = 0; ii < d->signalReplacements.size(); ++ii) {
443 QQuickReplaceSignalHandler *handler = d->signalReplacements.at(ii);
444
445 if (handler->property.isValid()) {
447 a.event = handler;
448 list << a;
449 }
450 }
451
452 for (int ii = 0; ii < d->expressions.size(); ++ii) {
453
455 const QString &property = e.name;
456 QQmlProperty prop = d->property(property);
457
458 if (prop.isValid()) {
460 a.restore = restoreEntryValues();
461 a.property = prop;
462 a.fromValue = a.property.read();
463 a.specifiedObject = d->object;
464 a.specifiedProperty = property;
465
467 QV4::Scope scope(qmlEngine(this)->handle());
469
470 if (d->isExplicit) {
471 // in this case, we don't want to assign a binding, per se,
472 // so we evaluate the expression and assign the result.
473 // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
474 // so that we can avoid creating then destroying the binding in this case.
475 std::unique_ptr<QQmlBinding> newBinding = nullptr;
476 if (e.binding && e.binding->isTranslationBinding()) {
477 newBinding.reset(QQmlBinding::createTranslationBinding(d->compilationUnit, e.binding, object(), context));
478 } else if (e.id != QQmlBinding::Invalid) {
479 newBinding.reset(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, d->compilationUnit->runtimeFunctions.at(e.id), object(), context, qmlCtxt));
480 } else {
481 newBinding.reset(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, e.expression, object(), context, e.url.toString(), e.line));
482 }
483 a.toValue = newBinding->evaluate();
484 } else {
485 QQmlAnyBinding newBinding = nullptr;
486 if (e.binding && e.binding->isTranslationBinding()) {
487 newBinding = QQmlAnyBinding::createTranslationBinding(prop, d->compilationUnit, e.binding, object(), context);
488 } else if (e.id != QQmlBinding::Invalid) {
489 newBinding = QQmlAnyBinding::createFromFunction(prop,
490 d->compilationUnit->runtimeFunctions.at(e.id),
491 object(), context, qmlCtxt);
492 } else {
493 newBinding = QQmlAnyBinding::createFromCodeString(prop, e.expression, object(), context, e.url.toString(), e.line);
494 }
495
496 a.toBinding = newBinding;
497 a.deletableToBinding = true;
498 }
499
500 list << a;
501 }
502 }
503
504 return list;
505}
506
527{
528 Q_D(const QQuickPropertyChanges);
529 return d->isExplicit;
530}
531
533{
535 if (e != d->isExplicit) {
536 d->isExplicit = e;
538 }
539}
540
542{
543 Q_D(const QQuickPropertyChanges);
544 typedef QPair<QString, QVariant> PropertyEntry;
545
546 for (const PropertyEntry &entry : d->properties) {
547 if (entry.first == name) {
548 return true;
549 }
550 }
551
552 return false;
553}
554
556{
557 Q_D(const QQuickPropertyChanges);
559
560 for (const ExpressionEntry &entry : d->expressions) {
561 if (entry.name == name) {
562 return true;
563 }
564 }
565
566 return false;
567}
568
570{
572}
573
575{
577 typedef QPair<QString, QVariant> PropertyEntry;
578
579 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
580 if (it->name == name) {
581 d->expressions.erase(it);
582 if (state() && state()->isStateActive()) {
584 d->property(name).write(value);
585 }
586
587 d->properties.append(PropertyEntry(name, value));
588 return;
589 }
590 }
591
592 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
593 if (it->first == name) {
594 it->second = value;
595 if (state() && state()->isStateActive())
596 d->property(name).write(value);
597 return;
598 }
599 }
600
601 QQuickStateAction action;
602 action.restore = restoreEntryValues();
603 action.property = d->property(name);
604 action.fromValue = action.property.read();
605 action.specifiedObject = object();
606 action.specifiedProperty = name;
607 action.toValue = value;
608
609 d->properties.append(PropertyEntry(name, value));
610 if (state() && state()->isStateActive()) {
611 state()->addEntryToRevertList(action);
613 if (oldBinding)
615 d->property(name).write(value);
616 }
617}
618
620{
623
624 bool hadValue = false;
625
626 for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
627 if (it->first == name) {
628 d->properties.erase(it);
629 hadValue = true;
630 break;
631 }
632 }
633
634 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
635 if (it->name == name) {
636 it->expression = expression;
637 if (state() && state()->isStateActive()) {
638 auto prop = d->property(name);
640 QString url;
641 int lineNumber = 0;
642 QQmlAnyBinding::createFromCodeString(prop, expression, object(), context, url, lineNumber).installOn(prop);
643 }
644 return;
645 }
646 }
647
648 // adding a new expression.
649 d->expressions.append(ExpressionEntry(name, nullptr, QQmlBinding::Invalid, expression, QUrl(), -1, -1));
650
651 const QString url;
652 const quint16 lineNumber = 0;
653 if (state() && state()->isStateActive()) {
654 if (hadValue) {
655 auto prop = d->property(name);
656 QQmlAnyBinding oldBinding = QQmlAnyBinding::takeFrom(prop);
657 if (oldBinding)
658 state()->changeBindingInRevertList(object(), name, oldBinding);
659
661 } else {
662 QQuickStateAction action;
663 action.restore = restoreEntryValues();
664 action.property = d->property(name);
665 action.fromValue = action.property.read();
666 action.specifiedObject = object();
667 action.specifiedProperty = name;
668
669 QQmlAnyBinding newBinding;
670 if (d->isExplicit) {
671 newBinding = QQmlBinding::create(
673 object(), QQmlContextData::get(qmlContext(this)));
674 } else {
675 const auto prop = action.property;
676 const auto context = QQmlContextData::get(qmlContext(this));
677 newBinding = QQmlAnyBinding::createFromCodeString(prop, expression, object(), context, url, lineNumber);
678 }
679 if (d->isExplicit) {
680 // don't assign the binding, merely evaluate the expression.
681 // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
682 // so that we can avoid creating then destroying the binding in this case.
683 action.toValue = static_cast<QQmlBinding *>(newBinding.asAbstractBinding())->evaluate();
684 } else {
685 // TODO: replace binding would be more efficient for new-style properties
687 newBinding.installOn(action.property);
688 action.toBinding = newBinding;
689 action.deletableToBinding = true;
690 state()->addEntryToRevertList(action);
691 }
692 }
693 }
694 // what about the signal handler?
695}
696
698{
699 Q_D(const QQuickPropertyChanges);
700 typedef QPair<QString, QVariant> PropertyEntry;
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
719{
721
722 for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
723 if (it->name == name) {
724 d->expressions.erase(it);
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(it);
734 return;
735 }
736 }
737}
738
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
754{
755 Q_D(const QQuickPropertyChanges);
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
768{
769 if (state())
771}
772
774{
775 if (state())
777}
778
780
781#include "moc_qquickpropertychanges_p.cpp"
Definition qlist.h:74
void clear()
Definition qlist.h:417
QVariant read(const QObject *obj) const
Reads the property's value from the given object.
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:153
\inmodule QtCore
Definition qobject.h:90
\inmodule QtCore
Definition qpointer.h:18
virtual void setEnabled(bool e, QQmlPropertyData::WriteFlags f=QQmlPropertyData::DontRemoveBinding)=0
QQmlAnyBinding is an abstraction over the various bindings in QML.
static QQmlAnyBinding createFromCodeString(const QQmlProperty &prop, const QString &code, QObject *obj, const QQmlRefPointer< QQmlContextData > &ctxt, const QString &url, quint16 lineNumber)
static void removeBindingFrom(QQmlProperty &prop)
static QQmlAnyBinding createTranslationBinding(const QQmlProperty &prop, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QV4::CompiledData::Binding *translationBinding, QObject *scopeObject=nullptr, QQmlRefPointer< QQmlContextData > context={})
static QQmlAnyBinding takeFrom(const QQmlProperty &prop)
Removes the binding from the property prop, and returns it as a QQmlAnyBinding if there was any.
QQmlAbstractBinding * asAbstractBinding() const
void installOn(const QQmlProperty &target, InterceptorMode mode=IgnoreInterceptors)
static QQmlAnyBinding createFromFunction(const QQmlProperty &prop, QV4::Function *function, QObject *obj, const QQmlRefPointer< QQmlContextData > &ctxt, QV4::ExecutionContext *scope)
static QQmlBinding * createTranslationBinding(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &unit, const QV4::CompiledData::Binding *binding, QObject *obj, const QQmlRefPointer< QQmlContextData > &ctxt)
static QQmlBinding * create(const QQmlPropertyData *, const QQmlScriptString &, QObject *, QQmlContext *)
static QQmlRefPointer< QQmlContextData > get(QQmlContext *context)
static QQmlData * get(QObjectPrivate *priv, bool create)
Definition qqmldata_p.h:199
quint16 lineNumber
Definition qqmldata_p.h:168
quint16 columnNumber
Definition qqmldata_p.h:169
QQmlContextData * outerContext
Definition qqmldata_p.h:149
static QQmlBoundSignalExpression * signalExpression(const QQmlProperty &that)
Returns the expression associated with this signal property, or 0 if no signal expression exists.
QQmlPropertyData core
static QQmlProperty create(QObject *target, const QString &propertyName, const QQmlRefPointer< QQmlContextData > &context, QQmlPropertyPrivate::InitFlags flags)
static void removeBinding(const QQmlProperty &that)
static void setSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *)
Set the signal expression associated with this signal property to expr.
static QQmlPropertyPrivate * get(const QQmlProperty &p)
static QQmlAbstractBinding * binding(QObject *, QQmlPropertyIndex index)
The QQmlProperty class abstracts accessing properties on objects created from QML.
bool isValid() const
Returns true if the QQmlProperty refers to a valid property, otherwise false.
QVariant read() const
Returns the property value.
Type type() const
Returns the type of the property.
QMetaProperty property() const
Returns the \l{QMetaProperty} {Qt property} associated with this QML property.
QML_ANONYMOUSQObject * object
bool isSignalProperty() const
Returns true if this QQmlProperty represents a QML signal property.
bool isWritable() const
Returns true if the property is writable, otherwise false.
T * data() const
QQmlRefPointer< T > & adopt(T *)
Takes ownership of other.
void verifyBindings(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QList< const QV4::CompiledData::Binding * > &props) override
void verifyList(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QV4::CompiledData::Binding *binding)
void applyBindings(QObject *obj, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QList< const QV4::CompiledData::Binding * > &bindings) override
ExpressionChange(const QString &_name, const QV4::CompiledData::Binding *_binding, QQmlBinding::Identifier _id, const QString &_expr, const QUrl &_url, int _line, int _column)
QQmlProperty property(const QString &)
QList< QQuickReplaceSignalHandler * > signalReplacements
void decodeBinding(const QString &propertyPrefix, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &qmlUnit, const QV4::CompiledData::Binding *binding)
QList< QPair< QString, QVariant > > properties
QList< const QV4::CompiledData::Binding * > bindings
QQmlRefPointer< QV4::ExecutableCompilationUnit > compilationUnit
QList< ExpressionChange > expressions
void changeExpression(const QString &name, const QString &expression)
void removeProperty(const QString &name)
bool containsProperty(const QString &name) const
QString expression(const QString &name) const
QVariant property(const QString &name) const
bool isExplicit() const
\qmlproperty bool QtQuick::PropertyChanges::explicit
bool containsExpression(const QString &name) const
bool containsValue(const QString &name) const
QVariant value(const QString &name) const
ActionList actions() override
void changeValue(const QString &name, const QVariant &value)
void restoreEntryValuesChanged()
\qmltype PropertyChanges \inqmlmodule QtQuick
void copyOriginals(QQuickStateActionEvent *other) override
QQmlRefPointer< QQmlBoundSignalExpression > expression
bool mayOverride(QQuickStateActionEvent *other) override
EventType type() const override
QQmlRefPointer< QQmlBoundSignalExpression > rewindExpression
QQmlRefPointer< QQmlBoundSignalExpression > reverseExpression
QQmlProperty property
QObject * specifiedObject
QString specifiedProperty
QQmlAnyBinding toBinding
QQuickState * state() const
bool isStateActive() const
void removeAllEntriesFromRevertList(QObject *target)
bool removeEntryFromRevertList(QObject *target, const QString &name)
void addEntryToRevertList(const QQuickStateAction &action)
void addEntriesToRevertList(const QList< QQuickStateAction > &actions)
bool changeBindingInRevertList(QObject *target, const QString &name, QQmlAnyBinding binding)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
\inmodule QtCore
Definition qurl.h:94
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1888
const CompiledObject * objectAt(int index) const
QString bindingValueAsString(const CompiledData::Binding *binding) const
\inmodule QtCore
Definition qvariant.h:64
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
double e
QSet< QString >::iterator it
Combined button and popup list for selecting options.
static void * context
std::pair< T1, T2 > QPair
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
GLsizei const GLfloat * v
[13]
GLuint64 GLenum void * handle
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLuint end
GLenum GLuint id
[7]
GLenum type
GLenum GLuint GLsizei const GLenum * props
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLenum GLenum GLsizei void GLsizei void * column
GLhandleARB obj
[2]
GLuint entry
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLfloat GLfloat p
[1]
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
QQmlEngine * qmlEngine(const QObject *obj)
Definition qqml.cpp:76
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:71
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
unsigned int quint32
Definition qtypes.h:45
unsigned short quint16
Definition qtypes.h:43
const char property[13]
Definition qwizard.cpp:101
QList< int > list
[14]
QUrl url("example.com")
[constructor-url-reference]
QSharedPointer< T > other(t)
[5]
engine evaluate("var myObject = new MyObject()")
[8]
\inmodule QtCore \reentrant
Definition qchar.h:17
union QV4::CompiledData::Binding::@543 value
double bindingValueAsNumber(const CompiledData::Binding *binding) const
const Binding * bindingTable() const
ExecutionContext * rootContext() const
static Heap::QmlContext * create(QV4::ExecutionContext *parent, QQmlRefPointer< QQmlContextData > context, QObject *scopeObject)
ExecutionEngine * engine
static bool isSignalPropertyName(const QString &name)