diff options
Diffstat (limited to 'src/qml/types')
-rw-r--r-- | src/qml/types/qqmlbind.cpp | 909 | ||||
-rw-r--r-- | src/qml/types/qqmlbind_p.h | 60 | ||||
-rw-r--r-- | src/qml/types/qqmlconnections.cpp | 299 | ||||
-rw-r--r-- | src/qml/types/qqmlconnections_p.h | 63 | ||||
-rw-r--r-- | src/qml/types/qqmllocaleenums_p.h | 48 | ||||
-rw-r--r-- | src/qml/types/qqmlloggingcategory.cpp | 146 | ||||
-rw-r--r-- | src/qml/types/qqmlloggingcategory_p.h | 71 | ||||
-rw-r--r-- | src/qml/types/qqmlmodelindexvaluetype.cpp | 68 | ||||
-rw-r--r-- | src/qml/types/qqmlmodelindexvaluetype_p.h | 164 | ||||
-rw-r--r-- | src/qml/types/qqmltimer.cpp | 42 | ||||
-rw-r--r-- | src/qml/types/qqmltimer_p.h | 52 | ||||
-rw-r--r-- | src/qml/types/types.pri | 22 |
12 files changed, 1258 insertions, 686 deletions
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index 6d762401d0..6991d642f5 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -1,117 +1,331 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmlbind_p.h" -#include <private/qqmlnullablevalue_p.h> -#include <private/qqmlproperty_p.h> +#include <private/qqmlanybinding_p.h> #include <private/qqmlbinding_p.h> +#include <private/qqmlcomponent_p.h> #include <private/qqmlmetatype_p.h> +#include <private/qqmlnullablevalue_p.h> +#include <private/qqmlproperty_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qv4persistent_p.h> +#include <private/qv4qmlcontext_p.h> +#include <private/qv4resolvedtypereference_p.h> -#include <qqmlengine.h> -#include <qqmlcontext.h> -#include <qqmlproperty.h> -#include <qqmlinfo.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlinfo.h> +#include <QtQml/qqmlproperty.h> +#include <QtQml/qqmlpropertymap.h> + +#include <QtCore/private/qobject_p.h> -#include <QtCore/qfile.h> #include <QtCore/qdebug.h> -#include <QtCore/qtimer.h> +#include <QtCore/qfile.h> #include <QtCore/qloggingcategory.h> - -#include <private/qobject_p.h> +#include <QtCore/qpointer.h> +#include <QtCore/qtimer.h> QT_BEGIN_NAMESPACE -Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval) +Q_STATIC_LOGGING_CATEGORY(lcQtQmlBindingRemoval, "qt.qml.binding.removal", QtWarningMsg) + +enum class QQmlBindEntryKind: quint8 { + V4Value, + Variant, + Binding, + None +}; + +/*! + * \internal + * QQmlBindEntryContent can store one of QV4::Value, QVariant, QQmlAnyBinding, or nothing, + * as denoted by QQmlBindEntryKind. It expects the calling code to know what is stored at + * any time. On each method invocation, the current kind has to be passed as last parameter + * and the new kind is returned. + */ +union QQmlBindEntryContent { + Q_DISABLE_COPY_MOVE(QQmlBindEntryContent) +public: + QQmlBindEntryContent() {} + ~QQmlBindEntryContent() {} + + [[nodiscard]] QQmlBindEntryKind set( + QQmlBindEntryContent &&other, QQmlBindEntryKind newKind, QQmlBindEntryKind oldKind) + { + silentDestroy(oldKind); + switch (newKind) { + case QQmlBindEntryKind::V4Value: + new (&v4Value) QV4::PersistentValue(std::move(other.v4Value)); + break; + case QQmlBindEntryKind::Variant: + new (&variant) QVariant(std::move(other.variant)); + break; + case QQmlBindEntryKind::Binding: + new (&binding) QQmlAnyBinding(std::move(other.binding)); + break; + case QQmlBindEntryKind::None: + break; + } + return newKind; + } + + [[nodiscard]] QQmlBindEntryKind set( + const QQmlBindEntryContent &other, QQmlBindEntryKind newKind, QQmlBindEntryKind oldKind) + { + silentDestroy(oldKind); + switch (newKind) { + case QQmlBindEntryKind::V4Value: + new (&v4Value) QV4::PersistentValue(other.v4Value); + break; + case QQmlBindEntryKind::Variant: + new (&variant) QVariant(other.variant); + break; + case QQmlBindEntryKind::Binding: + new (&binding) QQmlAnyBinding(other.binding); + break; + case QQmlBindEntryKind::None: + break; + } + return newKind; + } + + [[nodiscard]] QQmlBindEntryKind destroy(QQmlBindEntryKind kind) + { + switch (kind) { + case QQmlBindEntryKind::V4Value: + v4Value.~PersistentValue(); + break; + case QQmlBindEntryKind::Variant: + variant.~QVariant(); + break; + case QQmlBindEntryKind::Binding: + binding.~QQmlAnyBinding(); + break; + case QQmlBindEntryKind::None: + break; + } + return QQmlBindEntryKind::None; + } + + [[nodiscard]] QQmlBindEntryKind set(QVariant v, QQmlBindEntryKind oldKind) + { + silentDestroy(oldKind); + new (&variant) QVariant(std::move(v)); + return QQmlBindEntryKind::Variant; + } + + [[nodiscard]] QQmlBindEntryKind set(QV4::PersistentValue v, QQmlBindEntryKind oldKind) + { + silentDestroy(oldKind); + new (&v4Value) QV4::PersistentValue(std::move(v)); + return QQmlBindEntryKind::V4Value; + } + + [[nodiscard]] QQmlBindEntryKind set(QQmlAnyBinding v, QQmlBindEntryKind oldKind) + { + silentDestroy(oldKind); + new (&binding) QQmlAnyBinding(std::move(v)); + return QQmlBindEntryKind::Binding; + } + + QV4::PersistentValue v4Value; + QVariant variant; + QQmlAnyBinding binding; + +private: + void silentDestroy(QQmlBindEntryKind oldKind) + { + const QQmlBindEntryKind dead = destroy(oldKind); + Q_ASSERT(dead == QQmlBindEntryKind::None); + Q_UNUSED(dead); + } +}; + +/*! + * \internal + * QQmlBindEntry holds two QQmlBindEntryContent members, along with their kinds. + * The \l current content is the value or binding the Binding element installs on + * the target if enabled (that is, if \l{when}). The \l previous content is what + * the target holds before the Binding element installs its binding or value. It + * is restored if !\l{when}. The \l prop member holds the target property. + */ +struct QQmlBindEntry +{ + QQmlBindEntry() = default; + QQmlBindEntry(QQmlBindEntry &&other) noexcept : prop(std::move(other.prop)) + { + currentKind = current.set(std::move(other.current), other.currentKind, currentKind); + previousKind = previous.set(std::move(other.previous), other.previousKind, previousKind); + } + + QQmlBindEntry(const QQmlBindEntry &other) + : prop(other.prop) + { + currentKind = current.set(other.current, other.currentKind, currentKind); + previousKind = previous.set(other.previous, other.previousKind, previousKind); + } + + ~QQmlBindEntry() + { + currentKind = current.destroy(currentKind); + previousKind = previous.destroy(previousKind); + } + + QQmlBindEntry &operator=(QQmlBindEntry &&other) noexcept + { + if (this == &other) + return *this; + prop = std::move(other.prop); + currentKind = current.set(std::move(other.current), other.currentKind, currentKind); + previousKind = previous.set(std::move(other.previous), other.previousKind, previousKind); + return *this; + } + + QQmlBindEntry &operator=(const QQmlBindEntry &other) + { + if (this == &other) + return *this; + prop = other.prop; + currentKind = current.set(other.current, other.currentKind, currentKind); + previousKind = previous.set(other.previous, other.previousKind, previousKind); + return *this; + } + + + QQmlBindEntryContent current; + QQmlBindEntryContent previous; + QQmlProperty prop; + QQmlBindEntryKind currentKind = QQmlBindEntryKind::None; + QQmlBindEntryKind previousKind = QQmlBindEntryKind::None; + + void validate(QQmlBind *q) const; + void clearPrev(); + void setTarget(QQmlBind *q, const QQmlProperty &p); +}; class QQmlBindPrivate : public QObjectPrivate { public: QQmlBindPrivate() - : obj(nullptr) - , prevBind(QQmlAbstractBinding::Ptr()) - , prevIsVariant(false) + : when(true) , componentComplete(true) , delayed(false) , pendingEval(false) , restoreBinding(true) , restoreValue(true) , writingProperty(false) - {} + , lastIsTarget(false) + { + } ~QQmlBindPrivate() { } - QQmlNullableValue<bool> when; + // There can be multiple entries when using the generalized grouped + // property syntax. One is used for target/property/value. + QVarLengthArray<QQmlBindEntry, 1> entries; + + // The target object if using the \l target property QPointer<QObject> obj; + + // Any values we need to create a proxy for. This is necessary when + // using the \l delayed member on generalized grouped properties. See + // the note on \l delayed. + std::unique_ptr<QQmlPropertyMap> delayedValues; + + // The property name if using the \l property property. QString propName; - QQmlNullableValue<QJSValue> value; - QQmlProperty prop; - QQmlAbstractBinding::Ptr prevBind; - QV4::PersistentValue v4Value; - QVariant prevValue; - bool prevIsVariant:1; + + // Whether the binding is enabled. + bool when: 1; + + // Whether we have already parsed any generalized grouped properties + // we might need. bool componentComplete:1; + + // Whether we should run in "delayed" mode and proxy all values before + // applying them to the target. bool delayed:1; + + // In delayed mode, when using the target/property mode, the \l value + // is the proxy. Then pendingEval denotes that a timer is active to + // apply the value. We should not start another timer then. bool pendingEval:1; + + // Whether we should restore bindings on !when. + // TODO: Deprecate this and always do. bool restoreBinding:1; + + // Whether we should restore values on !when. + // TODO: Deprecate this and always do. bool restoreValue:1; - bool writingProperty: 1; - void validate(QObject *binding) const; - void clearPrev(); + // writingProperty tracks whether we are updating the target property + // when using target/property/value. We use this information to warn about + // binding removal if we detect the target property to be updated while we + // are not writing it. This doesn't remove the Binding after all. + // For generalized grouped properties, we don't have to do this as writing + // the target property does remove the binding, just like it removes any + // other binding. + bool writingProperty:1; + + // Whether the last entry is the the target property referred to by the + // \l target object and the \l property property. This will generally be + // the case when using \l target and \l property. + bool lastIsTarget:1; + + QQmlBindEntry *targetEntry(); + void validate(QQmlBind *binding) const; + void decodeBinding( + QQmlBind *q, const QString &propertyPrefix, QQmlData::DeferredData *deferredData, + const QV4::CompiledData::Binding *binding, + QQmlComponentPrivate::ConstructionState *immediateState); + void createDelayedValues(); + void onDelayedValueChanged(QString delayedName); + void evalDelayed(); + void buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState); }; -void QQmlBindPrivate::validate(QObject *binding) const +void QQmlBindEntry::validate(QQmlBind *q) const { - if (!obj || (when.isValid() && !when)) - return; + if (!prop.isWritable()) { + qmlWarning(q) << "Property '" << prop.name() << "' on " + << QQmlMetaType::prettyTypeName(prop.object()) << " is read-only."; + } +} - if (!prop.isValid()) { - qmlWarning(binding) << "Property '" << propName << "' does not exist on " << QQmlMetaType::prettyTypeName(obj) << "."; - return; +QQmlBindEntry *QQmlBindPrivate::targetEntry() +{ + if (!lastIsTarget) { + entries.append(QQmlBindEntry()); + lastIsTarget = true; } + return &entries.last(); +} - if (!prop.isWritable()) { - qmlWarning(binding) << "Property '" << propName << "' on " << QQmlMetaType::prettyTypeName(obj) << " is read-only."; +void QQmlBindPrivate::validate(QQmlBind *q) const +{ + if (!when) return; + + qsizetype iterationEnd = entries.size(); + if (lastIsTarget) { + if (obj) { + Q_ASSERT(!entries.isEmpty()); + const QQmlBindEntry &last = entries.last(); + if (!last.prop.isValid()) { + qmlWarning(q) << "Property '" << propName << "' does not exist on " + << QQmlMetaType::prettyTypeName(obj) << "."; + --iterationEnd; + } + } else { + --iterationEnd; + } } + + for (qsizetype i = 0; i < iterationEnd; ++i) + entries[i].validate(q); } /*! @@ -134,10 +348,10 @@ void QQmlBindPrivate::validate(QObject *binding) const For example, in a C++ application that maps an "app.enteredText" property into QML, you can use Binding to update the enteredText property. - \code + \qml TextEdit { id: myTextField; text: "Please type here..." } - Binding { target: app; property: "enteredText"; value: myTextField.text } - \endcode + Binding { app.enteredText: myTextField.text } + \endqml When \c{text} changes, the C++ property \c{enteredText} will update automatically. @@ -170,29 +384,25 @@ void QQmlBindPrivate::validate(QObject *binding) const The Binding type restores any previously set direct bindings on the property. - \sa {Qt QML} + \sa {Qt Qml} */ QQmlBind::QQmlBind(QObject *parent) : QObject(*(new QQmlBindPrivate), parent) { } -QQmlBind::~QQmlBind() -{ -} - /*! \qmlproperty bool QtQml::Binding::when This property holds when the binding is active. This should be set to an expression that evaluates to true when you want the binding to be active. - \code + \qml Binding { - target: contactName; property: 'text' - value: name; when: list.ListView.isCurrentItem + contactName.text: name + when: list.ListView.isCurrentItem } - \endcode + \endqml By default, any binding or value that was set perviously is restored when the binding becomes inactive. You can customize the restoration behavior using the \l restoreMode property. @@ -208,7 +418,7 @@ bool QQmlBind::when() const void QQmlBind::setWhen(bool v) { Q_D(QQmlBind); - if (!d->when.isNull && d->when == v) + if (d->when == v) return; d->when = v; @@ -218,9 +428,26 @@ void QQmlBind::setWhen(bool v) } /*! - \qmlproperty Object QtQml::Binding::target + \qmlproperty QtObject QtQml::Binding::target - The object to be updated. + The object to be updated. You need to use this property if the binding target + does not have an \c id attribute (for example, when the target is a singleton). + Otherwise, the following two pieces of code are equivalent: + + \qml + Binding { contactName.text: name } + \endqml + + \qml + Binding { + target: contactName + property: "text" + value: name + } + \endqml + + The former one is much more compact, but you cannot replace the target + object or property at run time. With the latter one you can. */ QObject *QQmlBind::object() { @@ -231,17 +458,33 @@ QObject *QQmlBind::object() void QQmlBind::setObject(QObject *obj) { Q_D(QQmlBind); - if (d->obj && d->when.isValid() && d->when) { + if (d->obj && d->when) { /* if we switch the object at runtime, we need to restore the previous binding on the old object before continuing */ d->when = false; eval(); d->when = true; } + /* if "when" and "target" depend on the same property, we might + end up here before we could have updated "when". So reevaluate + when manually here. + */ + const QQmlProperty whenProp(this, QLatin1StringView("when")); + const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(whenProp); + if (auto abstractBinding = potentialWhenBinding.asAbstractBinding()) { + QQmlBinding *binding = static_cast<QQmlBinding *>(abstractBinding); + if (binding->hasValidContext()) { + const auto boolType = QMetaType::fromType<bool>(); + bool when; + binding->evaluate(&when, boolType); + d->when = when; + } + } d->obj = obj; if (d->componentComplete) { setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this))); - d->validate(this); + if (d->when) + d->validate(this); } eval(); } @@ -252,7 +495,7 @@ void QQmlBind::setObject(QObject *obj) The property to be updated. This can be a group property if the expression results in accessing a - property of a \l {QML Basic Types}{value type}. For example: + property of a \l {QML Value Types}{value type}. For example: \qml Item { @@ -267,6 +510,14 @@ void QQmlBind::setObject(QObject *obj) value: 100 } \endqml + + You only need to use this property if you can't supply the binding target + declaratively. The following snippet of code is equivalent to the above + binding, but more compact: + + \qml + Binding { item.rectangle.x: 100 } + \endqml */ QString QQmlBind::property() const { @@ -277,7 +528,7 @@ QString QQmlBind::property() const void QQmlBind::setProperty(const QString &p) { Q_D(QQmlBind); - if (!d->propName.isEmpty() && d->when.isValid() && d->when) { + if (!d->propName.isEmpty() && d->when) { /* if we switch the property name at runtime, we need to restore the previous binding on the old object before continuing */ d->when = false; @@ -287,27 +538,35 @@ void QQmlBind::setProperty(const QString &p) d->propName = p; if (d->componentComplete) { setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this))); - d->validate(this); + if (d->when) + d->validate(this); } eval(); } /*! - \qmlproperty any QtQml::Binding::value + \qmlproperty var QtQml::Binding::value The value to be set on the target object and property. This can be a constant (which isn't very useful), or a bound expression. + + You only need to use this property if you can't supply the binding target + declaratively. Otherwise you can directly bind to the target. */ -QJSValue QQmlBind::value() const +QVariant QQmlBind::value() const { Q_D(const QQmlBind); - return d->value.value; + if (!d->lastIsTarget) + return QVariant(); + Q_ASSERT(d->entries.last().currentKind == QQmlBindEntryKind::Variant); + return d->entries.last().current.variant; } -void QQmlBind::setValue(const QJSValue &v) +void QQmlBind::setValue(const QVariant &v) { Q_D(QQmlBind); - d->value = v; + QQmlBindEntry *targetEntry = d->targetEntry(); + targetEntry->currentKind = targetEntry->current.set(v, targetEntry->currentKind); prepareEval(); } @@ -323,11 +582,19 @@ void QQmlBind::setValue(const QJSValue &v) \code Binding { - target: contactName; property: 'text' - value: givenName + " " + familyName; when: list.ListView.isCurrentItem + contactName.text.value: givenName + " " + familyName + when: list.ListView.isCurrentItem delayed: true } \endcode + + \note Using the \l delayed property incurs a run time cost as the Binding + element has to create a proxy for the value, so that it can delay its + application to the actual target. When using the \l target and + \l property properties, this cost is lower because the \l value + property can be re-used as proxy. When using the form shown above, + Binding will allocate a separate object with a dynamic meta-object to + hold the proxy values. */ bool QQmlBind::delayed() const { @@ -342,6 +609,28 @@ void QQmlBind::setDelayed(bool delayed) return; d->delayed = delayed; + if (!d->componentComplete) + return; + + d->delayedValues.reset(); + + QVarLengthArray<QQmlBindEntry, 1> oldEntries = std::move(d->entries); + d->entries.clear(); + d->buildBindEntries(this, nullptr); + + if (d->lastIsTarget) { + d->entries.append(std::move(oldEntries.last())); + oldEntries.pop_back(); + } + + for (qsizetype i = 0, end = oldEntries.size(); i < end; ++i) { + QQmlBindEntry &newEntry = d->entries[i]; + QQmlBindEntry &oldEntry = oldEntries[i]; + newEntry.previousKind = newEntry.previous.set( + std::move(oldEntry.previous), oldEntry.previousKind, newEntry.previousKind); + if (d->delayed && oldEntry.currentKind == QQmlBindEntryKind::Binding) + QQmlAnyBinding::removeBindingFrom(oldEntry.prop); + } if (!d->delayed) eval(); @@ -355,23 +644,18 @@ void QQmlBind::setDelayed(bool delayed) be restored when the binding is disabled. The possible values are: - \list - \li Binding.RestoreNone The original value is not restored at all - \li Binding.RestoreBinding The original value is restored if it was another - binding. In that case the old binding is in effect again. - \li Binding.RestoreValue The original value is restored if it was a plain - value rather than a binding. - \li Binding.RestoreBindingOrValue The original value is always restored. - \endlist - The default value is \c Binding.RestoreBindingOrValue. + \value Binding.RestoreNone The original value is not restored at all + \value Binding.RestoreBinding The original value is restored if it was another binding. + In that case the old binding is in effect again. + \value Binding.RestoreValue The original value is restored if it was a plain + value rather than a binding. + \value Binding.RestoreBindingOrValue The original value is always restored. - If you rely on any specific behavior regarding the restoration of plain - values when bindings get disabled you should migrate to explicitly set the - restoreMode. + The default value is \c Binding.RestoreBindingOrValue. - Reliance on a restoreMode that doesn't restore the previous binding or value - for a specific property results in a run-time warning. + \note This property exists for backwards compatibility with earlier versions + of Qt. Don't use it in new code. */ QQmlBind::RestorationMode QQmlBind::restoreMode() const { @@ -397,20 +681,24 @@ void QQmlBind::setRestoreMode(RestorationMode newMode) void QQmlBind::setTarget(const QQmlProperty &p) { Q_D(QQmlBind); + d->targetEntry()->setTarget(this, p); +} - if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { - if (QObject *oldObject = d->prop.object()) { - QMetaProperty prop = oldObject->metaObject()->property(d->prop.index()); - if (prop.hasNotifySignal()) { - QByteArray signal('2' + prop.notifySignal().methodSignature()); +void QQmlBindEntry::setTarget(QQmlBind *q, const QQmlProperty &p) +{ + if (Q_UNLIKELY(lcQtQmlBindingRemoval().isInfoEnabled())) { + if (QObject *oldObject = prop.object()) { + QMetaProperty metaProp = oldObject->metaObject()->property(prop.index()); + if (metaProp.hasNotifySignal()) { + QByteArray signal('2' + metaProp.notifySignal().methodSignature()); QObject::disconnect(oldObject, signal.constData(), - this, SLOT(targetValueChanged())); + q, SLOT(targetValueChanged())); } } - p.connectNotifySignal(this, SLOT(targetValueChanged())); + p.connectNotifySignal(q, SLOT(targetValueChanged())); } - d->prop = p; + prop = p; } void QQmlBind::classBegin() @@ -419,14 +707,285 @@ void QQmlBind::classBegin() d->componentComplete = false; } +static QQmlAnyBinding createBinding( + const QQmlProperty &prop, const QV4::CompiledData::Binding *binding, + const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, + const QQmlRefPointer<QQmlContextData> &contextData, + QObject *scopeObject) +{ + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_Translation: + case QV4::CompiledData::Binding::Type_TranslationById: + return QQmlAnyBinding::createTranslationBinding(prop, compilationUnit, binding, scopeObject); + case QV4::CompiledData::Binding::Type_Script: { + const QQmlBinding::Identifier id = binding->value.compiledScriptIndex; + if (id == QQmlBinding::Invalid) { + return QQmlAnyBinding::createFromCodeString( + prop, compilationUnit->bindingValueAsString(binding), scopeObject, + contextData, compilationUnit->finalUrlString(), binding->location.line()); + } + QV4::Scope scope(contextData->engine()->handle()); + QV4::Scoped<QV4::QmlContext> qmlCtxt( + scope, QV4::QmlContext::create( + scope.engine->rootContext(), contextData, scopeObject)); + return QQmlAnyBinding::createFromFunction( + prop, compilationUnit->runtimeFunctions.at(id), scopeObject, contextData, + qmlCtxt); + } + default: + break; + } + return QQmlAnyBinding(); +} + +static void initCreator( + QQmlData::DeferredData *deferredData, + const QQmlRefPointer<QQmlContextData> &contextData, + QQmlComponentPrivate::ConstructionState *immediateState) +{ + if (!immediateState->hasCreator()) { + immediateState->setCompletePending(true); + immediateState->initCreator( + deferredData->context->parent(), deferredData->compilationUnit, + contextData); + immediateState->creator()->beginPopulateDeferred(deferredData->context); + } +} + +void QQmlBindPrivate::decodeBinding( + QQmlBind *q, const QString &propertyPrefix, + QQmlData::DeferredData *deferredData, + const QV4::CompiledData::Binding *binding, + QQmlComponentPrivate::ConstructionState *immediateState) +{ + const QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit + = deferredData->compilationUnit; + const QString propertySuffix = compilationUnit->stringAt(binding->propertyNameIndex); + const QString propertyName = propertyPrefix + propertySuffix; + + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_AttachedProperty: + if (propertyPrefix.isEmpty()) { + // Top-level attached properties cannot be generalized grouped properties. + // Treat them as regular properties. + // ... unless we're not supposed to handle regular properties. Then ignore them. + if (!immediateState) + return; + + Q_ASSERT(compilationUnit->stringAt(compilationUnit->objectAt(binding->value.objectIndex) + ->inheritedTypeNameIndex).isEmpty()); + + const QV4::ResolvedTypeReference *typeReference + = compilationUnit->resolvedType(binding->propertyNameIndex); + Q_ASSERT(typeReference); + QQmlType attachedType = typeReference->type(); + if (!attachedType.isValid()) { + if (QQmlTypeLoader *typeLoader = compilationUnit->engine->typeLoader()) { + const QQmlTypeNameCache::Result result + = deferredData->context->imports()->query(propertySuffix, typeLoader); + if (!result.isValid()) { + qmlWarning(q).nospace() + << "Unknown name " << propertySuffix << ". The binding is ignored."; + return; + } + attachedType = result.type; + } + } + + QQmlContext *context = qmlContext(q); + QObject *attachedObject = qmlAttachedPropertiesObject( + q, attachedType.attachedPropertiesFunction( + QQmlEnginePrivate::get(context->engine()))); + if (!attachedObject) { + qmlWarning(q).nospace() <<"Could not create attached properties object '" + << attachedType.typeName() << "'"; + return; + } + + initCreator(deferredData, QQmlContextData::get(context), immediateState); + immediateState->creator()->populateDeferredInstance( + q, deferredData->deferredIdx, binding->value.objectIndex, attachedObject, + attachedObject, /*value type property*/ nullptr, binding); + return; + } + Q_FALLTHROUGH(); + case QV4::CompiledData::Binding::Type_GroupProperty: { + const QString pre = propertyName + u'.'; + const QV4::CompiledData::Object *subObj + = compilationUnit->objectAt(binding->value.objectIndex); + const QV4::CompiledData::Binding *subBinding = subObj->bindingTable(); + for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) + decodeBinding(q, pre, deferredData, subBinding, immediateState); + return; + } + default: + break; + } + + QQmlBindEntry entry; + QQmlContext *context = qmlContext(q); + const QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context); + entry.prop = QQmlPropertyPrivate::create(nullptr, propertyName, contextData, + QQmlPropertyPrivate::InitFlag::AllowId); + if (!entry.prop.isValid()) { + // Try again in the context of this object. If that works, it's a regular property. + // ... unless we're not supposed to handle regular properties. Then ignore it. + if (!immediateState) + return; + + QQmlProperty property = QQmlPropertyPrivate::create( + q, propertyName, contextData, QQmlPropertyPrivate::InitFlag::AllowSignal); + if (property.isValid()) { + initCreator(deferredData, contextData, immediateState); + immediateState->creator()->populateDeferredBinding( + property, deferredData->deferredIdx, binding); + } else { + qmlWarning(q).nospace() << "Unknown name " << propertyName + << ". The binding is ignored."; + } + return; + } + + const auto setVariant = [&entry](QVariant var) { + entry.currentKind = entry.current.set(std::move(var), entry.currentKind); + }; + + const auto setBinding = [&entry](QQmlAnyBinding binding) { + entry.currentKind = entry.current.set(binding, entry.currentKind); + }; + + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_AttachedProperty: + case QV4::CompiledData::Binding::Type_GroupProperty: + Q_UNREACHABLE(); // Handled above + break; + case QV4::CompiledData::Binding::Type_Translation: + case QV4::CompiledData::Binding::Type_TranslationById: + case QV4::CompiledData::Binding::Type_Script: + if (delayed) { + if (!delayedValues) + createDelayedValues(); + const QString delayedName = QString::number(entries.size()); + delayedValues->insert(delayedName, QVariant()); + QQmlProperty bindingTarget = QQmlProperty(delayedValues.get(), delayedName); + Q_ASSERT(bindingTarget.isValid()); + QQmlAnyBinding anyBinding = createBinding( + bindingTarget, binding, compilationUnit, contextData, q); + anyBinding.installOn(bindingTarget); + } else { + setBinding(createBinding(entry.prop, binding, compilationUnit, contextData, q)); + } + break; + case QV4::CompiledData::Binding::Type_String: + setVariant(compilationUnit->bindingValueAsString(binding)); + break; + case QV4::CompiledData::Binding::Type_Number: + setVariant(compilationUnit->bindingValueAsNumber(binding)); + break; + case QV4::CompiledData::Binding::Type_Boolean: + setVariant(binding->valueAsBoolean()); + break; + case QV4::CompiledData::Binding::Type_Null: + setVariant(QVariant::fromValue(nullptr)); + break; + case QV4::CompiledData::Binding::Type_Object: + case QV4::CompiledData::Binding::Type_Invalid: + break; + } + + entries.append(std::move(entry)); +} + +void QQmlBindPrivate::createDelayedValues() +{ + delayedValues = std::make_unique<QQmlPropertyMap>(); + QObject::connect( + delayedValues.get(), &QQmlPropertyMap::valueChanged, + delayedValues.get(), [this](QString delayedName, const QVariant &value) { + Q_UNUSED(value); + onDelayedValueChanged(std::move(delayedName)); + } + ); +} + +void QQmlBindPrivate::onDelayedValueChanged(QString delayedName) +{ + Q_ASSERT(delayed); + Q_ASSERT(delayedValues); + const QString pendingName = QStringLiteral("pending"); + QStringList pending = qvariant_cast<QStringList>((*delayedValues)[pendingName]); + if (componentComplete && pending.size() == 0) + QTimer::singleShot(0, delayedValues.get(), [this]() { evalDelayed(); }); + else if (pending.contains(delayedName)) + return; + + pending.append(std::move(delayedName)); + (*delayedValues)[pendingName].setValue(std::move(pending)); +} + +void QQmlBindPrivate::evalDelayed() +{ + if (!when || !delayedValues) + return; + + const QString pendingName = QStringLiteral("pending"); + const QStringList pending = qvariant_cast<QStringList>((*delayedValues)[pendingName]); + for (const QString &delayedName : pending) { + bool ok; + const int delayedIndex = delayedName.toInt(&ok); + Q_ASSERT(ok); + Q_ASSERT(delayedIndex >= 0 && delayedIndex < entries.size()); + entries[delayedIndex].prop.write((*delayedValues)[delayedName]); + } + (*delayedValues)[pendingName].setValue(QStringList()); +} + +void QQmlBindPrivate::buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState) +{ + QQmlData *data = QQmlData::get(q); + if (data && !data->deferredData.isEmpty()) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine()); + for (QQmlData::DeferredData *deferredData : data->deferredData) { + QMultiHash<int, const QV4::CompiledData::Binding *> *bindings = &deferredData->bindings; + if (deferredState) { + QQmlComponentPrivate::ConstructionState constructionState; + for (auto it = bindings->cbegin(); it != bindings->cend(); ++it) + decodeBinding(q, QString(), deferredData, *it, &constructionState); + + + if (constructionState.hasCreator()) { + ++ep->inProgressCreations; + constructionState.creator()->finalizePopulateDeferred(); + constructionState.appendCreatorErrors(); + deferredState->push_back(std::move(constructionState)); + } + } else { + for (auto it = bindings->cbegin(); it != bindings->cend(); ++it) + decodeBinding(q, QString(), deferredData, *it, nullptr); + } + } + + if (deferredState) { + data->releaseDeferredData(); + if (!deferredState->empty()) + QQmlComponentPrivate::completeDeferred(ep, deferredState); + } + } +} + void QQmlBind::componentComplete() { Q_D(QQmlBind); + QQmlComponentPrivate::DeferredState deferredState; + d->buildBindEntries(this, &deferredState); d->componentComplete = true; - if (!d->prop.isValid()) { - setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this))); - d->validate(this); + if (!d->propName.isEmpty() || d->obj) { + QQmlBindEntry *target = d->targetEntry(); + if (!target->prop.isValid()) + target->setTarget(this, QQmlProperty(d->obj, d->propName, qmlContext(this))); } + d->validate(this); + d->evalDelayed(); eval(); } @@ -442,72 +1001,110 @@ void QQmlBind::prepareEval() } } -void QQmlBindPrivate::clearPrev() +void QQmlBindEntry::clearPrev() { - prevBind = nullptr; - v4Value.clear(); - prevValue.clear(); - prevIsVariant = false; + previousKind = previous.destroy(previousKind); } void QQmlBind::eval() { Q_D(QQmlBind); d->pendingEval = false; - if (!d->prop.isValid() || d->value.isNull || !d->componentComplete) + if (!d->componentComplete) return; - if (d->when.isValid()) { + for (QQmlBindEntry &entry : d->entries) { + if (!entry.prop.isValid() || (entry.currentKind == QQmlBindEntryKind::None)) + continue; + if (!d->when) { //restore any previous binding - if (d->prevBind) { + switch (entry.previousKind) { + case QQmlBindEntryKind::Binding: if (d->restoreBinding) { - QQmlAbstractBinding::Ptr p = d->prevBind; - d->clearPrev(); // Do that before setBinding(), as setBinding() may recurse. - QQmlPropertyPrivate::setBinding(p.data()); + QQmlAnyBinding p = std::move(entry.previous.binding); + entry.clearPrev(); // Do that before setBinding(), as setBinding() may recurse. + p.installOn(entry.prop); } - } else if (!d->v4Value.isEmpty()) { + break; + case QQmlBindEntryKind::V4Value: if (d->restoreValue) { - auto propPriv = QQmlPropertyPrivate::get(d->prop); + QQmlAnyBinding::takeFrom(entry.prop); // we don't want to have a binding active + auto propPriv = QQmlPropertyPrivate::get(entry.prop); QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object); Q_ASSERT(vmemo); - vmemo->setVMEProperty(propPriv->core.coreIndex(), *d->v4Value.valueRef()); - d->clearPrev(); + vmemo->setVMEProperty(propPriv->core.coreIndex(), + *entry.previous.v4Value.valueRef()); + entry.clearPrev(); } - } else if (d->prevIsVariant) { + break; + case QQmlBindEntryKind::Variant: if (d->restoreValue) { - d->prop.write(d->prevValue); - d->clearPrev(); + QQmlAnyBinding::takeFrom(entry.prop); // we don't want to have a binding active + entry.prop.write(entry.previous.variant); + entry.clearPrev(); } + break; + case QQmlBindEntryKind::None: + break; } - return; + continue; } //save any set binding for restoration - if (!d->prevBind && d->v4Value.isEmpty() && !d->prevIsVariant) { - // try binding first - d->prevBind = QQmlPropertyPrivate::binding(d->prop); - - if (!d->prevBind) { // nope, try a V4 value next - auto propPriv = QQmlPropertyPrivate::get(d->prop); + if (entry.previousKind == QQmlBindEntryKind::None) { + // try binding first; we need to use takeFrom to properly unlink the binding + QQmlAnyBinding prevBind = QQmlAnyBinding::takeFrom(entry.prop); + if (prevBind) { + entry.previousKind = entry.previous.set(std::move(prevBind), entry.previousKind); + } else { + // nope, try a V4 value next + auto propPriv = QQmlPropertyPrivate::get(entry.prop); auto propData = propPriv->core; if (!propPriv->valueTypeData.isValid() && propData.isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object); Q_ASSERT(vmemo); auto retVal = vmemo->vmeProperty(propData.coreIndex()); - d->v4Value = QV4::PersistentValue(vmemo->engine, retVal); - } else { // nope, use the meta object to get a QVariant - d->prevValue = d->prop.read(); - d->prevIsVariant = true; + entry.previousKind = entry.previous.set( + QV4::PersistentValue(vmemo->engine, retVal), entry.previousKind); + } else { + // nope, use the meta object to get a QVariant + entry.previousKind = entry.previous.set(entry.prop.read(), entry.previousKind); } } } - QQmlPropertyPrivate::removeBinding(d->prop); + // NOTE: removeBinding has no effect on QProperty classes, but + // we already used takeBinding to remove it + QQmlPropertyPrivate::removeBinding(entry.prop); } + if (!d->when) + return; + d->writingProperty = true; - d->prop.write(d->value.value.toVariant()); + for (qsizetype i = 0, end = d->entries.size(); i != end; ++i) { + QQmlBindEntry &entry = d->entries[i]; + if (!entry.prop.isValid()) + continue; + switch (entry.currentKind) { + case QQmlBindEntryKind::Variant: + entry.prop.write(entry.current.variant); + break; + case QQmlBindEntryKind::Binding: + Q_ASSERT(!d->delayed); + entry.current.binding.installOn(entry.prop); + break; + case QQmlBindEntryKind::V4Value: { + auto propPriv = QQmlPropertyPrivate::get(entry.prop); + QQmlVMEMetaObject::get(propPriv->object)->setVMEProperty( + propPriv->core.coreIndex(), *entry.current.v4Value.valueRef()); + break; + } + case QQmlBindEntryKind::None: + break; + } + } d->writingProperty = false; } @@ -517,7 +1114,7 @@ void QQmlBind::targetValueChanged() if (d->writingProperty) return; - if (d->when.isValid() && !d->when) + if (!d->when) return; QUrl url; @@ -529,7 +1126,7 @@ void QQmlBind::targetValueChanged() line = ddata->lineNumber; } - qCInfo(lcBindingRemoval, + qCInfo(lcQtQmlBindingRemoval, "The target property of the Binding element created at %s:%d was changed from " "elsewhere. This does not overwrite the binding. The target property will still be " "updated when the value of the Binding element changes.", diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h index c709224c23..d4c93ebe0a 100644 --- a/src/qml/types/qqmlbind_p.h +++ b/src/qml/types/qqmlbind_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLBIND_H #define QQMLBIND_H @@ -51,14 +15,15 @@ // We mean it. // -#include <qqml.h> +#include <QtQmlMeta/qtqmlmetaexports.h> +#include <QtQml/qqml.h> #include <QtCore/qobject.h> QT_BEGIN_NAMESPACE class QQmlBindPrivate; -class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus +class Q_QMLMETA_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus { public: enum RestorationMode { @@ -75,17 +40,18 @@ private: Q_INTERFACES(QQmlPropertyValueSource) Q_PROPERTY(QObject *target READ object WRITE setObject) Q_PROPERTY(QString property READ property WRITE setProperty) - Q_PROPERTY(QJSValue value READ value WRITE setValue) + Q_PROPERTY(QVariant value READ value WRITE setValue) Q_PROPERTY(bool when READ when WRITE setWhen) - Q_PROPERTY(bool delayed READ delayed WRITE setDelayed REVISION 8) + Q_PROPERTY(bool delayed READ delayed WRITE setDelayed REVISION(2, 8)) Q_PROPERTY(RestorationMode restoreMode READ restoreMode WRITE setRestoreMode - NOTIFY restoreModeChanged REVISION 14) + NOTIFY restoreModeChanged REVISION(2, 14)) Q_ENUM(RestorationMode) QML_NAMED_ELEMENT(Binding) + QML_ADDED_IN_VERSION(2, 0) + Q_CLASSINFO("ImmediatePropertyNames", "objectName,target,property,value,when,delayed,restoreMode"); public: QQmlBind(QObject *parent=nullptr); - ~QQmlBind(); bool when() const; void setWhen(bool); @@ -96,8 +62,8 @@ public: QString property() const; void setProperty(const QString &); - QJSValue value() const; - void setValue(const QJSValue &); + QVariant value() const; + void setValue(const QVariant &); bool delayed() const; void setDelayed(bool); @@ -123,6 +89,4 @@ private Q_SLOTS: QT_END_NAMESPACE -QML_DECLARE_TYPE(QQmlBind) - #endif diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 1e801641e5..710adc3efa 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -1,71 +1,140 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmlconnections_p.h" -#include <private/qqmlexpression_p.h> -#include <private/qqmlproperty_p.h> #include <private/qqmlboundsignal_p.h> -#include <qqmlcontext.h> #include <private/qqmlcontext_p.h> +#include <private/qqmlexpression_p.h> +#include <private/qqmlproperty_p.h> +#include <private/qqmlsignalnames_p.h> #include <private/qqmlvmemetaobject_p.h> -#include <qqmlinfo.h> +#include <private/qv4jscall_p.h> +#include <private/qv4qobjectwrapper_p.h> + +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlinfo.h> #include <QtCore/qdebug.h> +#include <QtCore/qloggingcategory.h> #include <QtCore/qstringlist.h> #include <private/qobject_p.h> QT_BEGIN_NAMESPACE +Q_STATIC_LOGGING_CATEGORY(lcQmlConnections, "qt.qml.connections") + +// This is the equivalent of QQmlBoundSignal for C++ methods as as slots. +// If a type derived from QQmlConnnections is compiled using qmltc, the +// JavaScript functions it contains are turned into C++ methods and we cannot +// use QQmlBoundSignal to connect to those. +struct QQmlConnectionSlotDispatcher : public QtPrivate::QSlotObjectBase +{ + QV4::ExecutionEngine *v4 = nullptr; + QObject *receiver = nullptr; + + // Signals rarely have more than one argument. + QQmlMetaObject::ArgTypeStorage<2> signalMetaTypes; + QQmlMetaObject::ArgTypeStorage<2> slotMetaTypes; + + QMetaObject::Connection connection; + + int slotIndex = -1; + bool enabled = true; + + QQmlConnectionSlotDispatcher( + QV4::ExecutionEngine *v4, QObject *sender, int signalIndex, + QObject *receiver, int slotIndex, bool enabled) + : QtPrivate::QSlotObjectBase(&impl) + , v4(v4) + , receiver(receiver) + , slotIndex(slotIndex) + , enabled(enabled) + { + QMetaMethod signal = sender->metaObject()->method(signalIndex); + QQmlMetaObject::methodReturnAndParameterTypes(signal, &signalMetaTypes, nullptr); + + QMetaMethod slot = receiver->metaObject()->method(slotIndex); + QQmlMetaObject::methodReturnAndParameterTypes(slot, &slotMetaTypes, nullptr); + } + + template<typename ArgTypeStorage> + struct TypedFunction + { + Q_DISABLE_COPY_MOVE(TypedFunction) + public: + TypedFunction(const ArgTypeStorage *storage) : storage(storage) {} + + QMetaType returnMetaType() const { return storage->at(0); } + qsizetype parameterCount() const { return storage->size() - 1; } + QMetaType parameterMetaType(qsizetype i) const { return storage->at(i + 1); } + + private: + const ArgTypeStorage *storage; + }; + + static void impl(int which, QSlotObjectBase *base, QObject *, void **metaArgs, bool *ret) + { + switch (which) { + case Destroy: { + delete static_cast<QQmlConnectionSlotDispatcher *>(base); + break; + } + case Call: { + QQmlConnectionSlotDispatcher *self = static_cast<QQmlConnectionSlotDispatcher *>(base); + QV4::ExecutionEngine *v4 = self->v4; + if (!v4) + break; + + if (!self->enabled) + break; + + TypedFunction typedFunction(&self->slotMetaTypes); + QV4::coerceAndCall( + v4, &typedFunction, metaArgs, + self->signalMetaTypes.data(), self->signalMetaTypes.size() - 1, + [&](void **argv, int) { + self->receiver->metaObject()->metacall( + self->receiver, QMetaObject::InvokeMetaMethod, + self->slotIndex, argv); + }); + + if (v4->hasException) { + QQmlError error = v4->catchExceptionAsQmlError(); + if (QQmlEngine *qmlEngine = v4->qmlEngine()) { + QQmlEnginePrivate::get(qmlEngine)->warning(error); + } else { + QMessageLogger( + qPrintable(error.url().toString()), error.line(), nullptr) + .warning().noquote() + << error.toString(); + } + } + break; + } + case Compare: + // We're not implementing the Compare protocol here. It's insane. + // QQmlConnectionSlotDispatcher compares false to anything. We use + // the regular QObject::disconnect with QMetaObject::Connection. + *ret = false; + break; + case NumOperations: + break; + } + }; +}; + class QQmlConnectionsPrivate : public QObjectPrivate { public: - QQmlConnectionsPrivate() : target(nullptr), enabled(true), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {} + QList<QBiPointer<QQmlBoundSignal, QQmlConnectionSlotDispatcher>> boundsignals; + QQmlGuard<QObject> target; - QList<QQmlBoundSignal*> boundsignals; - QObject *target; - - bool enabled; - bool targetSet; - bool ignoreUnknownSignals; - bool componentcomplete; + bool enabled = true; + bool targetSet = false; + bool ignoreUnknownSignals = false; + bool componentcomplete = true; QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QList<const QV4::CompiledData::Binding *> bindings; @@ -85,7 +154,7 @@ public: \qml MouseArea { - onClicked: { foo(parameters) } + onClicked: (mouse)=> { foo(mouse) } } \endqml @@ -127,7 +196,13 @@ public: } \endqml - \sa {Qt QML} + \note For backwards compatibility you can also specify the signal handlers + without \c{function}, like you would specify them directly in the target + object. This is not recommended. If you specify one signal handler this way, + then all signal handlers specified as \c{function} in the same Connections + object are ignored. + + \sa {Qt Qml} */ QQmlConnections::QQmlConnections(QObject *parent) : QObject(*(new QQmlConnectionsPrivate), parent) @@ -136,10 +211,22 @@ QQmlConnections::QQmlConnections(QObject *parent) : QQmlConnections::~QQmlConnections() { + Q_D(QQmlConnections); + + // The slot dispatchers hold cyclic references to their connections. Clear them. + for (const auto &bound : std::as_const(d->boundsignals)) { + if (QQmlConnectionSlotDispatcher *dispatcher = bound.isT2() ? bound.asT2() : nullptr) { + // No need to explicitly disconnect anymore since 'this' is the receiver. + // But to be safe, explicitly break any cyclic references between the connection + // and the slot object. + dispatcher->connection = {}; + dispatcher->destroyIfLastRef(); + } + } } /*! - \qmlproperty Object QtQml::Connections::target + \qmlproperty QtObject QtQml::Connections::target This property holds the object that sends the signal. If this property is not set, the \c target defaults to the parent of the Connection. @@ -150,7 +237,7 @@ QQmlConnections::~QQmlConnections() QObject *QQmlConnections::target() const { Q_D(const QQmlConnections); - return d->targetSet ? d->target : parent(); + return d->targetSet ? d->target.data() : parent(); } class QQmlBoundSignalDeleter : public QObject @@ -169,13 +256,19 @@ void QQmlConnections::setTarget(QObject *obj) if (d->targetSet && d->target == obj) return; d->targetSet = true; // even if setting to 0, it is *set* - for (QQmlBoundSignal *s : qAsConst(d->boundsignals)) { + for (const auto &bound : std::as_const(d->boundsignals)) { // It is possible that target is being changed due to one of our signal // handlers -> use deleteLater(). - if (s->isNotifying()) - (new QQmlBoundSignalDeleter(s))->deleteLater(); - else - delete s; + if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() : nullptr) { + if (signal->isNotifying()) + (new QQmlBoundSignalDeleter(signal))->deleteLater(); + else + delete signal; + } else { + QQmlConnectionSlotDispatcher *dispatcher = bound.asT2(); + QObject::disconnect(std::exchange(dispatcher->connection, {})); + dispatcher->destroyIfLastRef(); + } } d->boundsignals.clear(); d->target = obj; @@ -205,8 +298,12 @@ void QQmlConnections::setEnabled(bool enabled) d->enabled = enabled; - for (QQmlBoundSignal *s : qAsConst(d->boundsignals)) - s->setEnabled(d->enabled); + for (const auto &bound : std::as_const(d->boundsignals)) { + if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() : nullptr) + signal->setEnabled(d->enabled); + else + bound.asT2()->enabled = enabled; + } emit enabledChanged(); } @@ -234,26 +331,29 @@ void QQmlConnections::setIgnoreUnknownSignals(bool ignore) void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) { - for (int ii = 0; ii < props.count(); ++ii) { + for (int ii = 0; ii < props.size(); ++ii) { const QV4::CompiledData::Binding *binding = props.at(ii); const QString &propName = compilationUnit->stringAt(binding->propertyNameIndex); - if (!propName.startsWith(QLatin1String("on")) || (propName.length() < 3 || !propName.at(2).isUpper())) { + if (!QQmlSignalNames::isHandlerName(propName)) { error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); return; } - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + if (binding->type() == QV4::CompiledData::Binding::Type_Script) + continue; + + if (binding->type() >= QV4::CompiledData::Binding::Type_Object) { const QV4::CompiledData::Object *target = compilationUnit->objectAt(binding->value.objectIndex); if (!compilationUnit->stringAt(target->inheritedTypeNameIndex).isEmpty()) error(binding, QQmlConnections::tr("Connections: nested objects not allowed")); else error(binding, QQmlConnections::tr("Connections: syntax error")); return; - } if (binding->type != QV4::CompiledData::Binding::Type_Script) { - error(binding, QQmlConnections::tr("Connections: script expected")); - return; } + + error(binding, QQmlConnections::tr("Connections: script expected")); + return; } } @@ -274,10 +374,10 @@ void QQmlConnections::connectSignals() if (d->bindings.isEmpty()) { connectSignalsToMethods(); } else { -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - qmlWarning(this) << tr("Implicitly defined onFoo properties in Connections are deprecated. " - "Use this syntax instead: function onFoo(<arguments>) { ... }"); -#endif + if (lcQmlConnections().isWarningEnabled()) { + qmlWarning(this) << tr("Implicitly defined onFoo properties in Connections are deprecated. " + "Use this syntax instead: function onFoo(<arguments>) { ... }"); + } connectSignalsToBindings(); } } @@ -291,44 +391,51 @@ void QQmlConnections::connectSignalsToMethods() if (!ddata) return; - QV4::ExecutionEngine *engine = ddata->context->engine->handle(); + QV4::ExecutionEngine *engine = ddata->context->engine()->handle(); - QQmlContextData *ctxtdata = ddata->outerContext; + QQmlRefPointer<QQmlContextData> ctxtdata = ddata->outerContext; for (int i = ddata->propertyCache->methodOffset(), end = ddata->propertyCache->methodOffset() + ddata->propertyCache->methodCount(); i < end; ++i) { - QQmlPropertyData *handler = ddata->propertyCache->method(i); - if (!handler || !handler->isVMEFunction()) + const QQmlPropertyData *handler = ddata->propertyCache->method(i); + if (!handler) continue; const QString propName = handler->name(this); QQmlProperty prop(target, propName); if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) { - int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); - auto *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this)); - signal->setEnabled(d->enabled); - QV4::Scope scope(engine); QV4::ScopedContext global(scope, engine->rootContext()); - QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(this); - Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this - - QV4::ScopedFunctionObject method(scope, vmeMetaObject->vmeMethod(handler->coreIndex())); - - QQmlBoundSignalExpression *expression = - ctxtdata ? new QQmlBoundSignalExpression( - target, signalIndex, ctxtdata, this, - method->as<QV4::FunctionObject>()->function()) - : nullptr; - - signal->takeExpression(expression); - d->boundsignals += signal; + if (QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(this)) { + int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); + auto *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this)); + signal->setEnabled(d->enabled); + + QV4::Scoped<QV4::JavaScriptFunctionObject> method( + scope, vmeMetaObject->vmeMethod(handler->coreIndex())); + + QQmlBoundSignalExpression *expression = ctxtdata + ? new QQmlBoundSignalExpression( + target, signalIndex, ctxtdata, this, method->function()) + : nullptr; + + signal->takeExpression(expression); + d->boundsignals += signal; + } else { + QQmlConnectionSlotDispatcher *slot = new QQmlConnectionSlotDispatcher( + scope.engine, target, prop.index(), + this, handler->coreIndex(), d->enabled); + slot->connection = QObjectPrivate::connect( + target, prop.index(), slot, Qt::AutoConnection); + slot->ref(); + d->boundsignals += slot; + } } else if (!d->ignoreUnknownSignals - && propName.startsWith(QLatin1String("on")) && propName.length() > 2 + && propName.startsWith(QLatin1String("on")) && propName.size() > 2 && propName.at(2).isUpper()) { qmlWarning(this) << tr("Detected function \"%1\" in Connections element. " "This is probably intended to be a signal handler but no " @@ -343,10 +450,10 @@ void QQmlConnections::connectSignalsToBindings() Q_D(QQmlConnections); QObject *target = this->target(); QQmlData *ddata = QQmlData::get(this); - QQmlContextData *ctxtdata = ddata ? ddata->outerContext : nullptr; + QQmlRefPointer<QQmlContextData> ctxtdata = ddata ? ddata->outerContext : nullptr; - for (const QV4::CompiledData::Binding *binding : qAsConst(d->bindings)) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); + for (const QV4::CompiledData::Binding *binding : std::as_const(d->bindings)) { + Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script); QString propName = d->compilationUnit->stringAt(binding->propertyNameIndex); QQmlProperty prop(target, propName); diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index 7bf688cf75..f0852cda73 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLCONNECTIONS_H #define QQMLCONNECTIONS_H @@ -51,9 +15,11 @@ // We mean it. // -#include <qqml.h> #include <private/qqmlcustomparser_p.h> +#include <QtQmlMeta/qtqmlmetaexports.h> +#include <QtQml/qqml.h> + #include <QtCore/qobject.h> #include <QtCore/qstring.h> @@ -62,19 +28,21 @@ QT_BEGIN_NAMESPACE class QQmlBoundSignal; class QQmlContext; class QQmlConnectionsPrivate; -class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatus +class Q_QMLMETA_EXPORT QQmlConnections : public QObject, public QQmlParserStatus { Q_OBJECT Q_DECLARE_PRIVATE(QQmlConnections) Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged REVISION 3) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged REVISION(2, 3)) Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) QML_NAMED_ELEMENT(Connections) + QML_ADDED_IN_VERSION(2, 0) + QML_CUSTOMPARSER public: - QQmlConnections(QObject *parent=nullptr); + QQmlConnections(QObject *parent = nullptr); ~QQmlConnections(); QObject *target() const; @@ -86,17 +54,18 @@ public: bool ignoreUnknownSignals() const; void setIgnoreUnknownSignals(bool ignore); +protected: + void classBegin() override; + void componentComplete() override; + Q_SIGNALS: void targetChanged(); - Q_REVISION(3) void enabledChanged(); + Q_REVISION(2, 3) void enabledChanged(); private: void connectSignals(); void connectSignalsToMethods(); void connectSignalsToBindings(); - - void classBegin() override; - void componentComplete() override; }; // TODO: Drop this class as soon as we can @@ -117,6 +86,4 @@ inline QQmlCustomParser *qmlCreateCustomParser<QQmlConnections>() QT_END_NAMESPACE -QML_DECLARE_TYPE(QQmlConnections) - #endif diff --git a/src/qml/types/qqmllocaleenums_p.h b/src/qml/types/qqmllocaleenums_p.h new file mode 100644 index 0000000000..771b74e5fb --- /dev/null +++ b/src/qml/types/qqmllocaleenums_p.h @@ -0,0 +1,48 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQMLLOCALEENUMS_H +#define QQMLLOCALEENUMS_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtqmlglobal_p.h> +#include <private/qqmllocale_p.h> + +#include <QtQmlMeta/qtqmlmetaexports.h> +#include <QtQml/qqml.h> + +QT_REQUIRE_CONFIG(qml_locale); + +QT_BEGIN_NAMESPACE + +// Derive again so that we don't expose QQmlLocale as two different QML types +// as that would be bad style. +struct Q_QMLMETA_EXPORT QQmlLocaleEnums : public QQmlLocale +{ + Q_GADGET +}; + +// Use QML_FOREIGN_NAMESPACE so that we can expose QQmlLocaleEnums as a namespace +// rather than a value type. +namespace QQmlLocaleEnumsForeign +{ +Q_NAMESPACE_EXPORT(Q_QMLMETA_EXPORT) +QML_NAMED_ELEMENT(Locale) +QML_ADDED_IN_VERSION(2, 2) +QML_FOREIGN_NAMESPACE(QQmlLocaleEnums) +}; + +QT_END_NAMESPACE + +#endif // QQMLLOCALEENUMS_H + diff --git a/src/qml/types/qqmlloggingcategory.cpp b/src/qml/types/qqmlloggingcategory.cpp new file mode 100644 index 0000000000..c18be74525 --- /dev/null +++ b/src/qml/types/qqmlloggingcategory.cpp @@ -0,0 +1,146 @@ +// Copyright (C) 2016 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qqmlloggingcategory_p.h" + +#include <QtQml/qqmlinfo.h> + +#include <memory> + +/*! + \qmltype LoggingCategory + \ingroup qml-utility-elements + \inqmlmodule QtQml + \brief Defines a logging category in QML. + \since 5.8 + + A logging category can be passed to console.log() and friends as the first argument. + If supplied to the logger the LoggingCategory's name will be used as logging category. + Otherwise the default logging category will be used. + + \qml + import QtQuick + + Item { + LoggingCategory { + id: category + name: "com.qt.category" + defaultLogLevel: LoggingCategory.Warning + } + + Component.onCompleted: { + console.log(category, "log message"); + console.warn(category, "warning message"); + } + } + \endqml + + By default this outputs only \c{com.qt.category: warning message}. The + \c{log message} is suppressed due to the \l{defaultLogLevel}. You can, + however, configure log levels for QML logging categories the same way + you can configure them for + \l{QLoggingCategory#configuring-categories}{QLoggingCategory}. + + \note As the creation of objects is expensive, it is encouraged to put the needed + LoggingCategory definitions into a singleton and import this where needed. + + \sa QLoggingCategory +*/ + +/*! + \qmlproperty string QtQml::LoggingCategory::name + + Holds the name of the logging category. + + \note This property needs to be set when declaring the LoggingCategory + and cannot be changed later. + + \sa QLoggingCategory::categoryName() +*/ + +/*! + \qmlproperty enumeration QtQml::LoggingCategory::defaultLogLevel + \since 5.12 + + Holds the default log level of the logging category. By default it is + created with the LoggingCategory.Debug log level. + + The following enumeration values are available: + \list + \li LoggingCategory.Debug + \li LoggingCategory.Info + \li LoggingCategory.Warning + \li LoggingCategory.Critical + \li LoggingCategory.Fatal + \endlist + + They mirror the values of the \l{QtMsgType} enumeration. + + \note This property needs to be set when declaring the LoggingCategory + and cannot be changed later. + + \sa QtMsgType +*/ + +QQmlLoggingCategory::QQmlLoggingCategory(QObject *parent) + : QQmlLoggingCategoryBase(parent) + , m_initialized(false) +{ +} + +QQmlLoggingCategory::~QQmlLoggingCategory() +{ +} + +QString QQmlLoggingCategory::name() const +{ + return QString::fromUtf8(m_name); +} + +QQmlLoggingCategory::DefaultLogLevel QQmlLoggingCategory::defaultLogLevel() const +{ + return m_defaultLogLevel; +} + +void QQmlLoggingCategory::classBegin() +{ +} + +void QQmlLoggingCategory::componentComplete() +{ + m_initialized = true; + if (m_name.isNull()) + qmlWarning(this) << QLatin1String("Declaring the name of a LoggingCategory is mandatory and cannot be changed later"); + else + setCategory(m_name.constData(), QtMsgType(m_defaultLogLevel)); +} + +void QQmlLoggingCategory::setDefaultLogLevel(DefaultLogLevel defaultLogLevel) +{ + if (m_defaultLogLevel == defaultLogLevel) + return; + + if (m_initialized) { + qmlWarning(this) << QLatin1String("The defaultLogLevel of a LoggingCategory cannot be changed after the component is completed"); + return; + } + + m_defaultLogLevel = defaultLogLevel; +} + +void QQmlLoggingCategory::setName(const QString &name) +{ + const QByteArray newName = name.toUtf8(); + + if (m_name == newName) + return; + + if (m_initialized) { + qmlWarning(this) << QLatin1String("The name of a LoggingCategory cannot be changed after the component is completed"); + return; + } + + m_name = newName; +} + +#include "moc_qqmlloggingcategory_p.cpp" diff --git a/src/qml/types/qqmlloggingcategory_p.h b/src/qml/types/qqmlloggingcategory_p.h new file mode 100644 index 0000000000..1f1f9abb63 --- /dev/null +++ b/src/qml/types/qqmlloggingcategory_p.h @@ -0,0 +1,71 @@ +// Copyright (C) 2016 Pelagicore AG +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQMLLOGGINGCATEGORY_P_H +#define QQMLLOGGINGCATEGORY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qqmlloggingcategorybase_p.h> + +#include <QtQmlMeta/qtqmlmetaexports.h> + +#include <QtQml/qqml.h> +#include <QtQml/qqmlparserstatus.h> + +#include <QtCore/private/qglobal_p.h> +#include <QtCore/qloggingcategory.h> +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +class Q_QMLMETA_EXPORT QQmlLoggingCategory : public QQmlLoggingCategoryBase, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION(2, 12)) + QML_NAMED_ELEMENT(LoggingCategory) + QML_ADDED_IN_VERSION(2, 8) + +public: + enum DefaultLogLevel { + Debug = QtDebugMsg, + Info = QtInfoMsg, + Warning = QtWarningMsg, + Critical = QtCriticalMsg, + Fatal = QtFatalMsg + }; + Q_ENUM(DefaultLogLevel); + + QQmlLoggingCategory(QObject *parent = nullptr); + virtual ~QQmlLoggingCategory(); + + DefaultLogLevel defaultLogLevel() const; + void setDefaultLogLevel(DefaultLogLevel defaultLogLevel); + QString name() const; + void setName(const QString &name); + + void classBegin() override; + void componentComplete() override; + +private: + QByteArray m_name; + DefaultLogLevel m_defaultLogLevel = Debug; + bool m_initialized; +}; + +QT_END_NAMESPACE + +#endif // QQMLLOGGINGCATEGORY_H diff --git a/src/qml/types/qqmlmodelindexvaluetype.cpp b/src/qml/types/qqmlmodelindexvaluetype.cpp deleted file mode 100644 index cbf2fef348..0000000000 --- a/src/qml/types/qqmlmodelindexvaluetype.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlmodelindexvaluetype_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \internal -*/ -QString QQmlModelIndexValueType::propertiesString(const QModelIndex &idx) -{ - if (!idx.isValid()) - return QLatin1String("()"); - return QString(QLatin1String("(%1,%2,0x%3,%4(0x%5))")) - .arg(idx.row()).arg(idx.column()).arg(idx.internalId(), 0, 16) - .arg(QLatin1String(idx.model()->metaObject()->className())).arg(quintptr(idx.model()), 0, 16); -} - -/*! - \internal -*/ -QString QQmlItemSelectionRangeValueType::toString() const -{ - return QString(QLatin1String("QItemSelectionRange(%1,%2)")) - .arg(reinterpret_cast<const QQmlPersistentModelIndexValueType *>(&v.topLeft())->toString()) - .arg(reinterpret_cast<const QQmlPersistentModelIndexValueType *>(&v.bottomRight())->toString()); -} - -QT_END_NAMESPACE - -#include "moc_qqmlmodelindexvaluetype_p.cpp" diff --git a/src/qml/types/qqmlmodelindexvaluetype_p.h b/src/qml/types/qqmlmodelindexvaluetype_p.h deleted file mode 100644 index f5b1699574..0000000000 --- a/src/qml/types/qqmlmodelindexvaluetype_p.h +++ /dev/null @@ -1,164 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLMODELINDEXVALUETYPE_P_H -#define QQMLMODELINDEXVALUETYPE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qabstractitemmodel.h> -#include <QtCore/qitemselectionmodel.h> - -QT_BEGIN_NAMESPACE - -struct QQmlModelIndexValueType -{ - QModelIndex v; - - Q_PROPERTY(int row READ row CONSTANT FINAL) - Q_PROPERTY(int column READ column CONSTANT FINAL) - Q_PROPERTY(QModelIndex parent READ parent FINAL) - Q_PROPERTY(bool valid READ isValid CONSTANT FINAL) - Q_PROPERTY(QAbstractItemModel *model READ model CONSTANT FINAL) - Q_PROPERTY(quint64 internalId READ internalId CONSTANT FINAL) - Q_GADGET - -public: - Q_INVOKABLE QString toString() const - { return QLatin1String("QModelIndex") + propertiesString(v); } - - inline int row() const Q_DECL_NOTHROW { return v.row(); } - inline int column() const Q_DECL_NOTHROW { return v.column(); } - inline QModelIndex parent() const { return v.parent(); } - inline bool isValid() const Q_DECL_NOTHROW { return v.isValid(); } - inline QAbstractItemModel *model() const Q_DECL_NOTHROW - { return const_cast<QAbstractItemModel *>(v.model()); } - quint64 internalId() const { return v.internalId(); } - - static QString propertiesString(const QModelIndex &idx); - - static QPersistentModelIndex toPersistentModelIndex(const QModelIndex &index) - { return QPersistentModelIndex(index); } -}; - -struct QQmlPersistentModelIndexValueType -{ - QPersistentModelIndex v; - - Q_PROPERTY(int row READ row FINAL) - Q_PROPERTY(int column READ column FINAL) - Q_PROPERTY(QModelIndex parent READ parent FINAL) - Q_PROPERTY(bool valid READ isValid FINAL) - Q_PROPERTY(QAbstractItemModel *model READ model FINAL) - Q_PROPERTY(quint64 internalId READ internalId FINAL) - Q_GADGET - -public: - Q_INVOKABLE QString toString() const - { return QLatin1String("QPersistentModelIndex") + QQmlModelIndexValueType::propertiesString(v); } - - inline int row() const { return v.row(); } - inline int column() const { return v.column(); } - inline QModelIndex parent() const { return v.parent(); } - inline bool isValid() const { return v.isValid(); } - inline QAbstractItemModel *model() const { return const_cast<QAbstractItemModel *>(v.model()); } - inline quint64 internalId() const { return v.internalId(); } - - static const QModelIndex &toModelIndex(const QPersistentModelIndex &index) - { return index; } -}; - -struct QQmlItemSelectionRangeValueType -{ - QItemSelectionRange v; - - Q_PROPERTY(int top READ top FINAL) - Q_PROPERTY(int left READ left FINAL) - Q_PROPERTY(int bottom READ bottom FINAL) - Q_PROPERTY(int right READ right FINAL) - Q_PROPERTY(int width READ width FINAL) - Q_PROPERTY(int height READ height FINAL) - Q_PROPERTY(QPersistentModelIndex topLeft READ topLeft FINAL) - Q_PROPERTY(QPersistentModelIndex bottomRight READ bottomRight FINAL) - Q_PROPERTY(QModelIndex parent READ parent FINAL) - Q_PROPERTY(bool valid READ isValid FINAL) - Q_PROPERTY(bool empty READ isEmpty FINAL) - Q_PROPERTY(QAbstractItemModel *model READ model FINAL) - Q_GADGET - -public: - Q_INVOKABLE QString toString() const; - Q_INVOKABLE inline bool contains(const QModelIndex &index) const - { return v.contains(index); } - Q_INVOKABLE inline bool contains(int row, int column, const QModelIndex &parentIndex) const - { return v.contains(row, column, parentIndex); } - Q_INVOKABLE inline bool intersects(const QItemSelectionRange &other) const - { return v.intersects(other); } - Q_INVOKABLE QItemSelectionRange intersected(const QItemSelectionRange &other) const - { return v.intersected(other); } - - inline int top() const { return v.top(); } - inline int left() const { return v.left(); } - inline int bottom() const { return v.bottom(); } - inline int right() const { return v.right(); } - inline int width() const { return v.width(); } - inline int height() const { return v.height(); } - inline QPersistentModelIndex &topLeft() const { return const_cast<QPersistentModelIndex &>(v.topLeft()); } - inline QPersistentModelIndex &bottomRight() const { return const_cast<QPersistentModelIndex &>(v.bottomRight()); } - inline QModelIndex parent() const { return v.parent(); } - inline QAbstractItemModel *model() const { return const_cast<QAbstractItemModel *>(v.model()); } - inline bool isValid() const { return v.isValid(); } - inline bool isEmpty() const { return v.isEmpty(); } -}; - -#undef QLISTVALUETYPE_INVOKABLE_API - -QT_END_NAMESPACE - -#endif // QQMLMODELINDEXVALUETYPE_P_H - diff --git a/src/qml/types/qqmltimer.cpp b/src/qml/types/qqmltimer.cpp index af2ff56f2a..fb3c53e880 100644 --- a/src/qml/types/qqmltimer.cpp +++ b/src/qml/types/qqmltimer.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmltimer_p.h" @@ -313,8 +277,6 @@ void QQmlTimer::componentComplete() \qmlsignal QtQml::Timer::triggered() This signal is emitted when the Timer times out. - - The corresponding handler is \c onTriggered. */ void QQmlTimer::ticked() { diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h index 0cd93e4659..f926262952 100644 --- a/src/qml/types/qqmltimer_p.h +++ b/src/qml/types/qqmltimer_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLTIMER_H #define QQMLTIMER_H @@ -51,18 +15,18 @@ // We mean it. // -#include <qqml.h> +#include <private/qtqmlglobal_p.h> +#include <QtQmlMeta/qtqmlmetaexports.h> +#include <QtQml/qqml.h> #include <QtCore/qobject.h> -#include <private/qtqmlglobal_p.h> - QT_REQUIRE_CONFIG(qml_animation); QT_BEGIN_NAMESPACE class QQmlTimerPrivate; -class Q_QML_PRIVATE_EXPORT QQmlTimer : public QObject, public QQmlParserStatus +class Q_QMLMETA_EXPORT QQmlTimer : public QObject, public QQmlParserStatus { Q_OBJECT Q_DECLARE_PRIVATE(QQmlTimer) @@ -72,7 +36,9 @@ class Q_QML_PRIVATE_EXPORT QQmlTimer : public QObject, public QQmlParserStatus Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged) Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged) Q_PROPERTY(QObject *parent READ parent CONSTANT) + Q_CLASSINFO("ParentProperty", "parent") QML_NAMED_ELEMENT(Timer) + QML_ADDED_IN_VERSION(2, 0) public: QQmlTimer(QObject *parent=nullptr); @@ -116,6 +82,4 @@ private Q_SLOTS: QT_END_NAMESPACE -QML_DECLARE_TYPE(QQmlTimer) - #endif diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri deleted file mode 100644 index 54cd8710b6..0000000000 --- a/src/qml/types/types.pri +++ /dev/null @@ -1,22 +0,0 @@ -SOURCES += \ - $$PWD/qqmlbind.cpp \ - $$PWD/qqmlconnections.cpp - -HEADERS += \ - $$PWD/qqmlbind_p.h \ - $$PWD/qqmlconnections_p.h - -qtConfig(qml-itemmodel) { - SOURCES += \ - $$PWD/qqmlmodelindexvaluetype.cpp - HEADERS += \ - $$PWD/qqmlmodelindexvaluetype_p.h -} - -qtConfig(qml-animation) { - SOURCES += \ - $$PWD/qqmltimer.cpp - - HEADERS += \ - $$PWD/qqmltimer_p.h -} |