From 625c881cc74f2552e211b9ba81ac3b0ed30e9947 Mon Sep 17 00:00:00 2001 From: Milian Wolff Date: Thu, 9 Jan 2014 17:21:39 +0100 Subject: Add declarative object-registration API to QML WebChannel. The new registeredObjects list property is now preferred over the old imparative registerObject/registerObjects API. Items that are added to the list need an attached WebChannel.id property which holds the identifier under which the object is published to remote clients. Change-Id: I96a8047b9a85e27f3fd48c900180c22ebd20eb35 Reviewed-by: Simon Hausmann --- examples/qtobject/main.qml | 16 +++--- src/imports/webchannel/qmlwebchannel.cpp | 70 ++++++++++++++++++++++++ src/imports/webchannel/qmlwebchannel.h | 30 +++++++++- src/imports/webchannel/qmlwebchannelattached.cpp | 67 +++++++++++++++++++++++ src/imports/webchannel/qmlwebchannelattached.h | 67 +++++++++++++++++++++++ src/imports/webchannel/webchannel.pro | 6 +- src/webchannel/qwebchannel.cpp | 6 ++ src/webchannel/qwebchannel.h | 3 +- tests/qml/tst_metaobjectpublisher.qml | 10 ++-- 9 files changed, 256 insertions(+), 19 deletions(-) create mode 100644 src/imports/webchannel/qmlwebchannelattached.cpp create mode 100644 src/imports/webchannel/qmlwebchannelattached.h diff --git a/examples/qtobject/main.qml b/examples/qtobject/main.qml index 7bfe714..d8ec54b 100644 --- a/examples/qtobject/main.qml +++ b/examples/qtobject/main.qml @@ -49,23 +49,21 @@ import QtWebKit.experimental 1.0 Rectangle { TestObjectFactory { id: factory + WebChannel.id: "testObjectFactory" } TestObject { id: testObject objectName: "initialTestObject" + WebChannel.id: objectName } WebChannel { id: webChannel - onInitialized: { - registerObjects({ - "testObjectFactory": factory, - "initialTestObject": testObject - }); - - webView.url = "qrc:/index.html?webChannelBaseUrl=" + webChannel.baseUrl; - } + registeredObjects: [ + factory, + testObject + ]; } width: 480 @@ -73,7 +71,7 @@ Rectangle { WebView { id: webView - url: "about:blank" + url: webChannel.baseUrl ? ("qrc:/index.html?webChannelBaseUrl=" + webChannel.baseUrl) : "about:blank"; anchors.fill: parent experimental.preferences.developerExtrasEnabled: true } diff --git a/src/imports/webchannel/qmlwebchannel.cpp b/src/imports/webchannel/qmlwebchannel.cpp index a7a6db0..22da1ac 100644 --- a/src/imports/webchannel/qmlwebchannel.cpp +++ b/src/imports/webchannel/qmlwebchannel.cpp @@ -45,6 +45,8 @@ #include "qwebchannel_p.h" #include "qmetaobjectpublisher_p.h" +#include + QmlWebChannel::QmlWebChannel(QObject *parent) : QWebChannel(parent) { @@ -73,3 +75,71 @@ bool QmlWebChannel::test_clientIsIdle() const { return d->publisher->clientIsIdle; } + +void QmlWebChannel::objectIdChanged(const QString &newId) +{ + const QmlWebChannelAttached *const attached = qobject_cast(sender()); + Q_ASSERT(attached); + Q_ASSERT(attached->parent()); + Q_ASSERT(m_registeredObjects.contains(attached->parent())); + + QObject *const object = attached->parent(); + const QString &oldId = d->publisher->registeredObjectIds.value(object); + + if (!oldId.isEmpty()) { + deregisterObject(object); + } + + registerObject(newId, object); +} + +QmlWebChannelAttached *QmlWebChannel::qmlAttachedProperties(QObject *obj) +{ + return new QmlWebChannelAttached(obj); +} + +QQmlListProperty QmlWebChannel::registeredObjects() +{ + return QQmlListProperty(this, 0, + registeredObjects_append, + registeredObjects_count, + registeredObjects_at, + registeredObjects_clear); +} + +void QmlWebChannel::registeredObjects_append(QQmlListProperty *prop, QObject *object) +{ + const QmlWebChannelAttached *const attached = qobject_cast( + qmlAttachedPropertiesObject(object, false /* don't create */)); + if (!attached) { + const QQmlContext *const context = qmlContext(object); + qWarning() << "Cannot register object" << context->nameForObject(object) << '(' << object << ") without attached WebChannel.id property. Did you forget to set it?"; + return; + } + QmlWebChannel *channel = static_cast(prop->object); + if (!attached->id().isEmpty()) { + // TODO: warning in such cases? + channel->registerObject(attached->id(), object); + } + channel->m_registeredObjects.append(object); + connect(attached, SIGNAL(idChanged(QString)), channel, SLOT(objectIdChanged(QString))); +} + +int QmlWebChannel::registeredObjects_count(QQmlListProperty *prop) +{ + return static_cast(prop->object)->m_registeredObjects.size(); +} + +QObject *QmlWebChannel::registeredObjects_at(QQmlListProperty *prop, int index) +{ + return static_cast(prop->object)->m_registeredObjects.at(index); +} + +void QmlWebChannel::registeredObjects_clear(QQmlListProperty *prop) +{ + QmlWebChannel *channel = static_cast(prop->object); + foreach (QObject *object, channel->m_registeredObjects) { + channel->deregisterObject(object); + } + return channel->m_registeredObjects.clear(); +} diff --git a/src/imports/webchannel/qmlwebchannel.h b/src/imports/webchannel/qmlwebchannel.h index b52bc8b..9e25b45 100644 --- a/src/imports/webchannel/qmlwebchannel.h +++ b/src/imports/webchannel/qmlwebchannel.h @@ -45,19 +45,45 @@ #include +#include "qmlwebchannelattached.h" + +#include + +#include +#include + +class QmlWebChannelAttached; + class QmlWebChannel : public QWebChannel { Q_OBJECT + Q_PROPERTY( QQmlListProperty registeredObjects READ registeredObjects ) public: QmlWebChannel(QObject *parent = 0); virtual ~QmlWebChannel(); - // TODO: replace by list property - Q_INVOKABLE void registerObjects(const QVariantMap &objects); + Q_INVOKABLE void registerObjects(const QVariantMap& objects); + QQmlListProperty registeredObjects(); // TODO: remove this by replacing QML with C++ tests Q_INVOKABLE bool test_clientIsIdle() const; + + static QmlWebChannelAttached* qmlAttachedProperties(QObject *obj); + +private slots: + void objectIdChanged(const QString &newId); + +private: + static void registeredObjects_append(QQmlListProperty *prop, QObject* item); + static int registeredObjects_count(QQmlListProperty *prop); + static QObject *registeredObjects_at(QQmlListProperty *prop, int index); + static void registeredObjects_clear(QQmlListProperty *prop); + + QVector m_registeredObjects; }; +QML_DECLARE_TYPE( QmlWebChannel ) +QML_DECLARE_TYPEINFO( QmlWebChannel, QML_HAS_ATTACHED_PROPERTIES ) + #endif // QMLWEBCHANNEL_H diff --git a/src/imports/webchannel/qmlwebchannelattached.cpp b/src/imports/webchannel/qmlwebchannelattached.cpp new file mode 100644 index 0000000..9a413fc --- /dev/null +++ b/src/imports/webchannel/qmlwebchannelattached.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff +** Contact: http://www.qt-project.org/legal +* +** This file is part of the QtWebChannel 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlwebchannelattached.h" + +QmlWebChannelAttached::QmlWebChannelAttached(QObject *parent) + : QObject(parent) +{ + +} + +QmlWebChannelAttached::~QmlWebChannelAttached() +{ + +} + +QString QmlWebChannelAttached::id() const +{ + return m_id; +} + +void QmlWebChannelAttached::setId(const QString &id) +{ + if (id != m_id) { + m_id = id; + emit idChanged(id); + } +} diff --git a/src/imports/webchannel/qmlwebchannelattached.h b/src/imports/webchannel/qmlwebchannelattached.h new file mode 100644 index 0000000..c705c25 --- /dev/null +++ b/src/imports/webchannel/qmlwebchannelattached.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff +** Contact: http://www.qt-project.org/legal +* +** This file is part of the QtWebChannel 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLWEBCHANNELATTACHED_H +#define QMLWEBCHANNELATTACHED_H + +#include + +class QmlWebChannelAttached : public QObject +{ + Q_OBJECT + + Q_PROPERTY( QString id READ id WRITE setId NOTIFY idChanged FINAL ) +public: + explicit QmlWebChannelAttached(QObject *parent = 0); + virtual ~QmlWebChannelAttached(); + + QString id() const; + void setId(const QString &id); + +signals: + void idChanged(const QString &id); + +private: + QString m_id; +}; + +#endif // QMLWEBCHANNELATTACHED_H diff --git a/src/imports/webchannel/webchannel.pro b/src/imports/webchannel/webchannel.pro index 3051077..c1959f5 100644 --- a/src/imports/webchannel/webchannel.pro +++ b/src/imports/webchannel/webchannel.pro @@ -5,9 +5,11 @@ VPATH += ../../webchannel SOURCES += \ plugin.cpp \ - qmlwebchannel.cpp + qmlwebchannel.cpp \ + qmlwebchannelattached.cpp HEADERS += \ - qmlwebchannel.h + qmlwebchannel.h \ + qmlwebchannelattached.h load(qml_plugin) diff --git a/src/webchannel/qwebchannel.cpp b/src/webchannel/qwebchannel.cpp index f7a2c5f..70c8f35 100644 --- a/src/webchannel/qwebchannel.cpp +++ b/src/webchannel/qwebchannel.cpp @@ -123,6 +123,12 @@ void QWebChannel::registerObject(const QString &id, QObject *object) d->publisher->registerObject(id, object); } +void QWebChannel::deregisterObject(QObject *object) +{ + // handling of deregistration is analogously to handling of a destroyed signal + d->publisher->signalEmitted(object, s_destroyedSignalIndex, QVariantList() << QVariant::fromValue(object)); +} + bool QWebChannel::blockUpdates() const { return d->publisher->blockUpdates; diff --git a/src/webchannel/qwebchannel.h b/src/webchannel/qwebchannel.h index 22d87a7..4f464b3 100644 --- a/src/webchannel/qwebchannel.h +++ b/src/webchannel/qwebchannel.h @@ -77,7 +77,8 @@ public: * TODO: This must be called, before clients are initialized. */ void registerObjects(const QHash &objects); - void registerObject(const QString &id, QObject *object); + Q_INVOKABLE void registerObject(const QString &id, QObject *object); + Q_INVOKABLE void deregisterObject(QObject *object); /** * @return true when property updates are blocked, false otherwise. diff --git a/tests/qml/tst_metaobjectpublisher.qml b/tests/qml/tst_metaobjectpublisher.qml index cc679ad..e8b1c1e 100644 --- a/tests/qml/tst_metaobjectpublisher.qml +++ b/tests/qml/tst_metaobjectpublisher.qml @@ -51,11 +51,14 @@ WebChannelTest { { lastMethodArg = arg; } + + WebChannel.id: "myObj" } QtObject { id: myOtherObj property var foo: 1 property var bar: 1 + WebChannel.id: "myOtherObj" } QtObject { id: myFactory @@ -65,6 +68,7 @@ WebChannelTest { lastObj = component.createObject(myFactory, {objectName: id}); return lastObj; } + WebChannel.id: "myFactory" } Component { @@ -80,11 +84,7 @@ WebChannelTest { function initTestCase() { - webChannel.registerObjects({ - "myObj": myObj, - "myOtherObj": myOtherObj, - "myFactory": myFactory - }); + webChannel.registeredObjects = [myObj, myOtherObj, myFactory]; } function awaitInit() -- cgit v1.2.1