From 4ccfeb32c34371a3b5385ecd8b07f99a4abd6078 Mon Sep 17 00:00:00 2001 From: Konrad Rosenbaum Date: Sat, 2 Nov 2013 14:16:51 +0100 Subject: [PATCH] Enable QML to handle widgets. Added a qmlwidgethandler plugin that is dynamically loaded if QML encounters a widget that needs to be included in a hierarchy of QML objects. This plugin then calls QWidget::setParent to avoid a direct dependency between QML libs and QtWidgets. Widgets cannot have Layouts as parents, since layouts are not widgets themselves. Instead other routines will have to take care of proper parentage. --- src/plugins/plugins.pro | 3 + .../widgets/qmlwidgethandler/qmlwidgethandler.pro | 12 +++ .../widgets/qmlwidgethandler/qqmlwidgethandler.cpp | 67 +++++++++++++++++ .../widgets/qmlwidgethandler/qqmlwidgethandler.h | 65 +++++++++++++++++ src/plugins/widgets/widgets.pro | 3 + src/qml/qml/qqmlglobal.cpp | 56 ++++++++++++++ src/qml/qml/qqmlglobal_p.h | 5 ++ src/qml/qml/qqmlvme.cpp | 11 ++-- src/qml/qml/qqmlwidgethandlerplugin_p.h | 77 ++++++++++++++++++++ 9 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 src/plugins/widgets/qmlwidgethandler/qmlwidgethandler.pro create mode 100644 src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.cpp create mode 100644 src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.h create mode 100644 src/plugins/widgets/widgets.pro create mode 100644 src/qml/qml/qqmlwidgethandlerplugin_p.h diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 9ef8c7a..1887077 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -3,3 +3,6 @@ SUBDIRS += qmltooling contains(QT_CONFIG, accessibility) { SUBDIRS += accessible } +!contains(QT_CONFIG, no-widgets) { + SUBDIRS += widgets +} diff --git a/src/plugins/widgets/qmlwidgethandler/qmlwidgethandler.pro b/src/plugins/widgets/qmlwidgethandler/qmlwidgethandler.pro new file mode 100644 index 0000000..dfb4f4a --- /dev/null +++ b/src/plugins/widgets/qmlwidgethandler/qmlwidgethandler.pro @@ -0,0 +1,12 @@ +TARGET = qmlwidgethandler +QT = qml-private core-private v8-private widgets + +PLUGIN_TYPE = qmlwidgets +PLUGIN_CLASS_NAME = QQmlWidgetHandler +load(qt_plugin) + +SOURCES += \ + qqmlwidgethandler.cpp + +HEADERS += \ + qqmlwidgethandler.h diff --git a/src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.cpp b/src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.cpp new file mode 100644 index 0000000..27791af --- /dev/null +++ b/src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Konrad Rosenbaum . +** Contact: http://www.qt-project.org/legal +** +** 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 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 "qqmlwidgethandler.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QQmlWidgetHandler::QQmlWidgetHandler() +{ +} + +QQmlWidgetHandler::~QQmlWidgetHandler() +{ +} + +bool QQmlWidgetHandler::setWidgetParent(QObject*widget, QObject*parent) +{ + //see if both objects are widgets, if so: reparent and return success + if (widget->isWidgetType() && parent->isWidgetType()) { + static_cast(widget)->setParent(static_cast(parent)); + return true; + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.h b/src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.h new file mode 100644 index 0000000..390ee46 --- /dev/null +++ b/src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Konrad Rosenbaum . +** Contact: http://www.qt-project.org/legal +** +** 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 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 QQMLWIDGETHANDLER_H +#define QQMLWIDGETHANDLER_H + +#include + +QT_BEGIN_NAMESPACE + +class QQmlWidgetHandler : public QObject, public QQmlWidgetHandlerPlugin +{ + Q_OBJECT + Q_DISABLE_COPY(QQmlWidgetHandler) + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlWidgetHandlerPlugin") + Q_INTERFACES(QQmlWidgetHandlerPlugin) + +public: + QQmlWidgetHandler(); + ~QQmlWidgetHandler(); + + virtual bool setWidgetParent(QObject*widget, QObject*parent); +}; + +QT_END_NAMESPACE + +#endif // QQMLWIDGETHANDLER_H diff --git a/src/plugins/widgets/widgets.pro b/src/plugins/widgets/widgets.pro new file mode 100644 index 0000000..7e9604b --- /dev/null +++ b/src/plugins/widgets/widgets.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += qmlwidgethandler \ No newline at end of file diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index 607ee4d..f4fceb5 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -40,11 +40,15 @@ ****************************************************************************/ #include +#include #include #include #include #include +#include +#include +#include QT_BEGIN_NAMESPACE @@ -444,4 +448,56 @@ void QQmlApplication::setVersion(const QString &arg) emit versionChanged(); //Note that we don't get notified if it's changed from C++ } +DEFINE_BOOL_CONFIG_OPTION(qmlWidgetHandlerVerbose, QML_WIDGETHANDLER_VERBOSE) + +void QQml_setParent_widget(QObject *object, QObject *parent) +{ + Q_ASSERT(object); + Q_ASSERT(parent); + static QQmlWidgetHandlerPlugin*plugin=0; +#ifndef QT_NO_LIBRARY + if(plugin==0) { + QStringList pluginCandidates; + static const QString pluginName="qmlwidgethandler"; + + const QStringList paths = QCoreApplication::libraryPaths(); + foreach (const QString &libPath, paths) { + const QDir dir(libPath + QLatin1String("/qmlwidgets")); + if (dir.exists()) { + QStringList plugins(dir.entryList(QDir::Files)); + foreach (const QString &pluginPath, plugins) { + if (QFileInfo(pluginPath).fileName().contains(pluginName)) + pluginCandidates << dir.absoluteFilePath(pluginPath); + } + } + } + + foreach (const QString &pluginPath, pluginCandidates) { + QPluginLoader loader(pluginPath); + if (!loader.load()) { + if (qmlWidgetHandlerVerbose()) + qDebug() << "QML: Error while loading: " << loader.errorString(); + continue; + } + if (QObject *instance = loader.instance()) + plugin = qobject_cast(instance); + + if (plugin) { + if (qmlWidgetHandlerVerbose()) + qDebug() << "QML: WidgetHandlerPlugin successfully loaded."; + break; + } else { + loader.unload(); + } + } + + if (!plugin && qmlWidgetHandlerVerbose()) + qWarning("QML: unable to load the widget handler while trying to reparent a QML controlled widget!"); + } +#endif + Q_ASSERT_X(plugin, "QQml_setParent_widget", "Cannot reparent QML owned widgets without qmlwidgethandler plugin."); + if (!plugin->setWidgetParent(object, parent)) + qWarning("QML: failed to reparent widget"); +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 114c076..e58f9b1 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -222,6 +222,11 @@ inline void QQml_setParent_noEvent(QObject *object, QObject *parent) static_cast(object)->setParent_noEvent(parent); } +/*! + Assuming the \a object and \a parent are widgets, this calls the QWidget::setParent + method. If necessary first loading the plugin that handles this case. +*/ +void QQml_setParent_widget(QObject *object, QObject *parent); class QQmlValueType; class QV8Engine; diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index 736ed5f..eab014d 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -615,12 +615,13 @@ QObject *QQmlVME::run(QList *errors, } if (!objects.isEmpty()) { QObject *parent = objects.at(objects.count() - 1 - (instr.parentToSuper?1:0)); -#if 0 // ### refactor - if (o->isWidgetType() && parent->isWidgetType()) - static_cast(o)->setParent(static_cast(parent)); - else -#endif + if (o->isWidgetType()) { + if (parent->isWidgetType()) + QQml_setParent_widget(o, parent); + //else: parent is probably a layout + } else { QQml_setParent_noEvent(o, parent); + } ddata->parentFrozen = true; } objects.push(o); diff --git a/src/qml/qml/qqmlwidgethandlerplugin_p.h b/src/qml/qml/qqmlwidgethandlerplugin_p.h new file mode 100644 index 0000000..a9c5754 --- /dev/null +++ b/src/qml/qml/qqmlwidgethandlerplugin_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Konrad Rosenbaum . +** Contact: http://www.qt-project.org/legal +** +** 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 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 QQMLWIDGETHANDLERPLUGIN_H +#define QQMLWIDGETHANDLERPLUGIN_H + +#include +#include + +// +// 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. +// + +QT_BEGIN_NAMESPACE + + +class Q_QML_PRIVATE_EXPORT QQmlWidgetHandlerPlugin +{ +public: + QQmlWidgetHandlerPlugin() {} + virtual ~QQmlWidgetHandlerPlugin() {} + + virtual bool setWidgetParent(QObject*widget, QObject*parent)=0; +}; + +#define QQmlWidgetHandlerPlugin_iid "org.qt-project.Qt.QQmlWidgetHandlerPlugin" + +Q_DECLARE_INTERFACE(QQmlWidgetHandlerPlugin, QQmlWidgetHandlerPlugin_iid) + +QT_END_NAMESPACE + +#endif // QQMLWIDGETHANDLERPLUGIN_H -- 1.7.2.5