Enable QML to handle widgets. b5.1.1 patch
authorKonrad Rosenbaum <konrad@silmor.de>
Sat, 2 Nov 2013 13:16:51 +0000 (14:16 +0100)
committerKonrad Rosenbaum <konrad@silmor.de>
Thu, 28 Nov 2013 20:31:27 +0000 (21:31 +0100)
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
src/plugins/widgets/qmlwidgethandler/qmlwidgethandler.pro [new file with mode: 0644]
src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.cpp [new file with mode: 0644]
src/plugins/widgets/qmlwidgethandler/qqmlwidgethandler.h [new file with mode: 0644]
src/plugins/widgets/widgets.pro [new file with mode: 0644]
src/qml/qml/qqmlglobal.cpp
src/qml/qml/qqmlglobal_p.h
src/qml/qml/qqmlvme.cpp
src/qml/qml/qqmlwidgethandlerplugin_p.h [new file with mode: 0644]

index 9ef8c7a..1887077 100644 (file)
@@ -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 (file)
index 0000000..dfb4f4a
--- /dev/null
@@ -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 (file)
index 0000000..27791af
--- /dev/null
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Konrad Rosenbaum <konrad@silmor.de>.
+** 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 <QtCore/qplugin.h>
+#include <QtWidgets/qwidget.h>
+
+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<QWidget*>(widget)->setParent(static_cast<QWidget*>(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 (file)
index 0000000..390ee46
--- /dev/null
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Konrad Rosenbaum <konrad@silmor.de>.
+** 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 <QtQml/private/qqmlwidgethandlerplugin_p.h>
+
+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 (file)
index 0000000..7e9604b
--- /dev/null
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS += qmlwidgethandler
\ No newline at end of file
index 607ee4d..f4fceb5 100644 (file)
 ****************************************************************************/
 
 #include <private/qqmlglobal_p.h>
+#include <private/qqmlwidgethandlerplugin_p.h>
 
 #include <QtCore/qvariant.h>
 #include <QtCore/qstringlist.h>
 #include <QtCore/qdebug.h>
 #include <QtCore/QCoreApplication>
+#include <QtCore/qpluginloader.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdir.h>
 
 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<QQmlWidgetHandlerPlugin*>(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
index 114c076..e58f9b1 100644 (file)
@@ -222,6 +222,11 @@ inline void QQml_setParent_noEvent(QObject *object, QObject *parent)
     static_cast<QQmlGraphics_DerivedObject *>(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;
index 736ed5f..eab014d 100644 (file)
@@ -615,12 +615,13 @@ QObject *QQmlVME::run(QList<QQmlError> *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<QWidget*>(o)->setParent(static_cast<QWidget*>(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 (file)
index 0000000..a9c5754
--- /dev/null
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Konrad Rosenbaum <konrad@silmor.de>.
+** 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 <QtQml/qtqmlglobal.h>
+#include <private/qqmlglobal_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.
+//
+
+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