WIP: Shared QML engine for plugins

parent ee4dbad6
......@@ -157,11 +157,10 @@ set(SRCS ${SRCS}
plugins/plugins.cpp
plugins/speeddial.cpp
plugins/ocssupport.cpp
plugins/qml/qmlplugincontext.cpp
plugins/qml/qmlpluginloader.cpp
plugins/qml/qmlplugin.cpp
plugins/qml/qmlplugins.cpp
plugins/qml/qmlplugininterface.cpp
plugins/qml/qmlengine.cpp
plugins/qml/qmlstaticdata.cpp
plugins/qml/api/bookmarks/qmlbookmarktreenode.cpp
plugins/qml/api/bookmarks/qmlbookmarks.cpp
......
......@@ -25,7 +25,8 @@
#include "../config.h"
#include "desktopfile.h"
#include "qml/qmlplugins.h"
#include "qml/qmlplugin.h"
#include "qml/qmlpluginloader.h"
#include "qml/qmlplugininterface.h"
#include <iostream>
......@@ -61,6 +62,7 @@ Plugins::Plugins(QObject* parent)
if (!MainApplication::isTestModeEnabled()) {
loadPythonSupport();
}
QmlPlugins::registerQmlTypes();
}
QList<Plugins::Plugin> Plugins::availablePlugins()
......@@ -279,7 +281,7 @@ void Plugins::loadAvailablePlugins()
plugin = loadPythonPlugin(pluginPath);
} else if (type == QL1S("Extension/Qml")) {
// QmlPlugin
plugin = QmlPlugin::loadPlugin(pluginPath);
plugin = loadQmlPlugin(pluginPath);
} else {
qWarning() << "Invalid type" << type << "of" << pluginPath << "plugin";
}
......@@ -370,7 +372,7 @@ Plugins::Plugin Plugins::loadPlugin(const QString &id)
return loadPythonPlugin(name);
case Plugin::QmlPlugin:
return QmlPlugin::loadPlugin(name);
return loadQmlPlugin(name);
default:
return Plugin();
......@@ -429,6 +431,29 @@ Plugins::Plugin Plugins::loadPythonPlugin(const QString &name)
return f(name);
}
Plugins::Plugin Plugins::loadQmlPlugin(const QString &name)
{
QString fullPath;
if (QFileInfo(name).isAbsolute()) {
fullPath = name;
} else {
fullPath = DataPaths::locate(DataPaths::Plugins, name);
if (fullPath.isEmpty()) {
qWarning() << "QML plugin" << name << "not found";
return Plugins::Plugin();
}
}
Plugins::Plugin plugin;
plugin.type = Plugins::Plugin::QmlPlugin;
plugin.pluginId = QSL("qml:%1").arg(QFileInfo(name).fileName());
plugin.pluginPath = fullPath;
DesktopFile desktopFile(fullPath + QSL("/metadata.desktop"));
plugin.pluginSpec = Plugins::createSpec(desktopFile);
plugin.data = QVariant::fromValue(new QmlPluginLoader(plugin));
return plugin;
}
bool Plugins::initPlugin(PluginInterface::InitState state, Plugin *plugin)
{
if (!plugin) {
......@@ -449,7 +474,7 @@ bool Plugins::initPlugin(PluginInterface::InitState state, Plugin *plugin)
break;
case Plugin::QmlPlugin:
QmlPlugin::initPlugin(plugin);
initQmlPlugin(plugin);
break;
default:
......@@ -508,3 +533,24 @@ void Plugins::initPythonPlugin(Plugin *plugin)
f(plugin);
}
void Plugins::initQmlPlugin(Plugin *plugin)
{
Q_ASSERT(plugin->type == Plugins::Plugin::QmlPlugin);
const QString name = plugin->pluginSpec.name;
auto qmlPluginLoader = plugin->data.value<QmlPluginLoader*>();
if (!qmlPluginLoader) {
qWarning() << "Failed to cast from data";
return;
}
qmlPluginLoader->createComponent();
if (!qmlPluginLoader->instance()) {
qWarning() << "Failed to create component for" << name << "plugin:" << qmlPluginLoader->errorString();
return;
}
plugin->instance = qobject_cast<PluginInterface*>(qmlPluginLoader->instance());
}
......@@ -114,10 +114,12 @@ private:
Plugin loadInternalPlugin(const QString &name);
Plugin loadSharedLibraryPlugin(const QString &name);
Plugin loadPythonPlugin(const QString &name);
Plugin loadQmlPlugin(const QString &name);
bool initPlugin(PluginInterface::InitState state, Plugin *plugin);
void initInternalPlugin(Plugin *plugin);
void initSharedLibraryPlugin(Plugin *plugin);
void initPythonPlugin(Plugin *plugin);
void initQmlPlugin(Plugin *plugin);
void registerAvailablePlugin(const Plugin &plugin);
......
......@@ -21,10 +21,11 @@
#include "statusbar.h"
#include "pluginproxy.h"
#include "qml/api/fileutils/qmlfileutils.h"
#include "qml/qmlengine.h"
#include "qml/qmlplugincontext.h"
#include "qml/qmlstaticdata.h"
#include <QQuickWidget>
#include <QQmlContext>
#include <QQmlEngine>
QmlBrowserAction::QmlBrowserAction(QObject *parent)
: QObject(parent)
......@@ -217,13 +218,10 @@ void QmlBrowserActionButton::setIcon(const QString &icon)
if (!m_popup) {
return;
}
auto qmlEngine = qobject_cast<QmlEngine*>(m_popup->creationContext()->engine());
if (!qmlEngine) {
return;
}
const QString pluginPath = qmlEngine->extensionPath();
QIcon qicon = QmlStaticData::instance().getIcon(m_iconUrl, pluginPath);
#if 0
QIcon qicon = QmlStaticData::instance().getIcon(m_iconUrl, QmlPluginContext::contextForObject(this)->pluginPath());
AbstractButtonInterface::setIcon(qicon);
#endif
}
void QmlBrowserActionButton::setBadgeText(const QString &badgeText)
......
......@@ -18,30 +18,24 @@
#include "qmlaction.h"
#include "qztools.h"
#include "qml/api/fileutils/qmlfileutils.h"
#include "qml/qmlengine.h"
#include "qml/qmlplugincontext.h"
#include "qml/qmlstaticdata.h"
#include <QQmlEngine>
QmlAction::QmlAction(QAction *action, QmlEngine *engine, QObject *parent)
QmlAction::QmlAction(QAction *action, QObject *parent)
: QObject(parent)
, m_action(action)
{
QmlEngine *qmlEngine = qobject_cast<QmlEngine*>(engine);
m_pluginPath = qmlEngine->extensionPath();
Q_ASSERT(m_action);
connect(m_action, &QAction::triggered, this, &QmlAction::triggered);
}
void QmlAction::setProperties(const QVariantMap &map)
{
if (!m_action) {
return;
}
for (auto it = map.cbegin(); it != map.cend(); it++) {
const QString key = it.key();
if (key == QSL("icon")) {
QString iconPath = map.value(key).toString();
QIcon icon = QmlStaticData::instance().getIcon(iconPath, m_pluginPath);
QIcon icon = QmlStaticData::instance().getIcon(iconPath, QmlPluginContext::contextForObject(this)->pluginPath());
m_action->setIcon(icon);
} else if (key == QSL("shortcut")) {
m_action->setShortcut(QKeySequence(map.value(key).toString()));
......@@ -51,7 +45,7 @@ void QmlAction::setProperties(const QVariantMap &map)
}
}
void QmlAction::update(const QVariantMap &map)
void QmlAction::update(const QVariantMap &map)
{
setProperties(map);
}
......@@ -21,7 +21,7 @@
#include <QAction>
#include <QVariantMap>
class QmlEngine;
class QQmlEngine;
/**
* @brief The class exposing Action API to QML
......@@ -30,7 +30,7 @@ class QmlAction : public QObject
{
Q_OBJECT
public:
explicit QmlAction(QAction *action, QmlEngine *engine, QObject *parent = nullptr);
explicit QmlAction(QAction *action, QObject *parent = nullptr);
void setProperties(const QVariantMap &map);
/**
* @brief Updates the properties of the action
......@@ -46,5 +46,4 @@ Q_SIGNALS:
private:
QAction *m_action = nullptr;
QString m_pluginPath;
};
......@@ -18,61 +18,51 @@
#include "qmlmenu.h"
#include "qztools.h"
#include "qml/api/fileutils/qmlfileutils.h"
#include "qml/qmlengine.h"
#include "qml/qmlplugincontext.h"
#include "qml/qmlstaticdata.h"
QmlMenu::QmlMenu(QMenu *menu, QQmlEngine *engine, QObject *parent)
#include <QQmlEngine>
QmlMenu::QmlMenu(QMenu *menu, QObject *parent)
: QObject(parent)
, m_menu(menu)
{
QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
m_engine = qobject_cast<QmlEngine*>(engine);
m_pluginPath = m_engine->extensionPath();
Q_ASSERT(m_menu);
connect(m_menu, &QMenu::triggered, this, &QmlMenu::triggered);
}
QmlAction *QmlMenu::addAction(const QVariantMap &map)
QJSValue QmlMenu::addAction(const QVariantMap &map)
{
if (!m_menu) {
return nullptr;
}
QAction *action = new QAction();
QmlAction *qmlAction = new QmlAction(action, m_engine, this);
QmlAction *qmlAction = new QmlAction(action, this);
QQmlEngine::setContextForObject(qmlAction, QmlPluginContext::contextForObject(this));
action->setParent(qmlAction);
qmlAction->setProperties(map);
m_menu->addAction(action);
return qmlAction;
return qmlEngine(this)->newQObject(qmlAction);
}
QmlMenu *QmlMenu::addMenu(const QVariantMap &map)
QJSValue QmlMenu::addMenu(const QVariantMap &map)
{
if (!m_menu) {
return nullptr;
}
QMenu *newMenu = new QMenu();
for (auto it = map.cbegin(); it != map.cend(); it++) {
const QString key = it.key();
if (key == QSL("icon")) {
const QString iconPath = map.value(key).toString();
const QIcon icon = QmlStaticData::instance().getIcon(iconPath, m_pluginPath);
const QIcon icon = QmlStaticData::instance().getIcon(iconPath, QmlPluginContext::contextForObject(this)->pluginPath());
newMenu->setIcon(icon);
continue;
}
newMenu->setProperty(key.toUtf8(), map.value(key));
}
m_menu->addMenu(newMenu);
QmlMenu *newQmlMenu = new QmlMenu(newMenu, m_engine, this);
return newQmlMenu;
QmlMenu *newQmlMenu = new QmlMenu(newMenu, this);
QQmlEngine::setContextForObject(newQmlMenu, QmlPluginContext::contextForObject(this));
connect(newQmlMenu, &QObject::destroyed, newMenu, &QObject::deleteLater);
return qmlEngine(this)->newQObject(newQmlMenu);
}
void QmlMenu::addSeparator()
{
if (!m_menu) {
return;
}
m_menu->addSeparator();
}
......@@ -18,10 +18,9 @@
#pragma once
#include "qmlaction.h"
#include <QMenu>
#include <QQmlEngine>
class QmlEngine;
#include <QMenu>
#include <QJSValue>
/**
* @brief The class exposing WebView contextmenu to QML as Menu API
......@@ -30,7 +29,7 @@ class QmlMenu : public QObject
{
Q_OBJECT
public:
explicit QmlMenu(QMenu *menu, QQmlEngine *engine, QObject *parent = nullptr);
explicit QmlMenu(QMenu *menu, QObject *parent = nullptr);
/**
* @brief Adds action to menu
* @param A JavaScript object containing properties for action.
......@@ -38,14 +37,14 @@ public:
* and shortcut in form string.
* @return action of type [QmlAction](@ref QmlAction)
*/
Q_INVOKABLE QmlAction *addAction(const QVariantMap &map);
Q_INVOKABLE QJSValue addAction(const QVariantMap &map);
/**
* @brief Adds sub-menu to menu
* @param A JavaScript object containing properties of menu.
* The icon property must be in form of url of the path.
* @return menu of type [QmlMenu](@ref QmlMenu)
*/
Q_INVOKABLE QmlMenu *addMenu(const QVariantMap &map);
Q_INVOKABLE QJSValue addMenu(const QVariantMap &map);
/**
* @brief Adds a separator to menu
*/
......@@ -59,6 +58,4 @@ Q_SIGNALS:
private:
QMenu *m_menu = nullptr;
QString m_pluginPath;
QmlEngine *m_engine = nullptr;
};
......@@ -20,11 +20,12 @@
#include "qztools.h"
#include "sidebar.h"
#include "qml/api/fileutils/qmlfileutils.h"
#include "qml/qmlengine.h"
#include "qml/qmlplugincontext.h"
#include "qml/qmlstaticdata.h"
#include <QAction>
#include <QQuickWidget>
#include <QQmlContext>
#include <QQmlEngine>
QmlSideBar::QmlSideBar(QObject *parent)
: QObject(parent)
......@@ -137,13 +138,10 @@ QAction *QmlSideBarHelper::createMenuAction()
if (!m_item) {
return action;
}
auto qmlEngine = qobject_cast<QmlEngine*>(m_item->creationContext()->engine());
if (qmlEngine) {
return action;
}
const QString pluginPath = qmlEngine->extensionPath();
const QIcon icon = QmlStaticData::instance().getIcon(m_iconUrl, pluginPath);
#if 0
const QIcon icon = QmlStaticData::instance().getIcon(m_iconUrl, QmlPluginContext::contextForObject(this)->pluginPath());
action->setIcon(icon);
#endif
return action;
}
......
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2018 Anmol Gautam <tarptaeya@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "qmlplugin.h"
#include "qmlplugins.h"
#include "qmlpluginloader.h"
#include "datapaths.h"
#include "desktopfile.h"
#include <QFileInfo>
#include <QDir>
QmlPlugin::QmlPlugin()
{
}
Plugins::Plugin QmlPlugin::loadPlugin(const QString &name)
{
static bool qmlSupportLoaded = false;
if (!qmlSupportLoaded) {
QmlPlugins::registerQmlTypes();
qmlSupportLoaded = true;
}
QString fullPath;
if (QFileInfo(name).isAbsolute()) {
fullPath = name;
} else {
fullPath = DataPaths::locate(DataPaths::Plugins, name);
if (fullPath.isEmpty()) {
qWarning() << "QML plugin" << name << "not found";
return Plugins::Plugin();
}
}
Plugins::Plugin plugin;
plugin.type = Plugins::Plugin::QmlPlugin;
plugin.pluginId = QSL("qml:%1").arg(QFileInfo(name).fileName());
plugin.pluginPath = fullPath;
DesktopFile desktopFile(fullPath + QSL("/metadata.desktop"));
plugin.pluginSpec = Plugins::createSpec(desktopFile);
plugin.data = QVariant::fromValue(new QmlPluginLoader(plugin.pluginSpec.name, fullPath));
return plugin;
}
void QmlPlugin::initPlugin(Plugins::Plugin *plugin)
{
Q_ASSERT(plugin->type == Plugins::Plugin::QmlPlugin);
const QString name = plugin->pluginSpec.name;
auto qmlPluginLoader = plugin->data.value<QmlPluginLoader*>();
if (!qmlPluginLoader) {
qWarning() << "Failed to cast from data";
return;
}
qmlPluginLoader->createComponent();
if (!qmlPluginLoader->instance()) {
qWarning().noquote() << "Failed to create component for" << name << "plugin:" << qmlPluginLoader->component()->errorString();
return;
}
plugin->instance = qobject_cast<PluginInterface*>(qmlPluginLoader->instance());
}
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2018 Anmol Gautam <tarptaeya@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#pragma once
#include "plugins.h"
class QmlPlugin
{
public:
explicit QmlPlugin();
static Plugins::Plugin loadPlugin(const QString &name);
static void initPlugin(Plugins::Plugin *plugin);
};
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2018 Anmol Gautam <tarptaeya@gmail.com>
* Copyright (C) 2019 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -15,29 +15,44 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "qmlengine.h"
#include "qmlplugincontext.h"
QmlEngine::QmlEngine(QObject *parent)
: QQmlEngine(parent)
#include <QtQml>
#include <QFileInfo>
QmlPluginContext::QmlPluginContext(const Plugins::Plugin &plugin, QQmlEngine *engine, QObject *parent)
: QQmlContext(engine, parent)
, m_plugin(plugin)
{
}
QString QmlEngine::extensionName()
QString QmlPluginContext::pluginPath() const
{
return m_extensionName;
return m_plugin.pluginPath;
}
void QmlEngine::setExtensionName(const QString &name)
QString QmlPluginContext::pluginName() const
{
m_extensionName = name;
return QFileInfo(m_plugin.pluginPath).fileName();
}
QString QmlEngine::extensionPath()
Plugins::Plugin QmlPluginContext::plugin() const
{
return m_extensionPath;
return m_plugin;
}
void QmlEngine::setExtensionPath(const QString &path)
// static
QmlPluginContext *QmlPluginContext::contextForObject(const QObject *object)
{
m_extensionPath = path;
QQmlContext *c = qmlContext(object);
while (c) {
QmlPluginContext *p = qobject_cast<QmlPluginContext*>(c);
if (p) {
return p;
}
c = c->parentContext();
}
qCritical() << "Failed to get plugin context for object" << object;
Q_UNREACHABLE();
return nullptr;
}
/* ============================================================
* Falkon - Qt web browser
* Copyright (C) 2018 Anmol Gautam <tarptaeya@gmail.com>
* Copyright (C) 2019 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -17,18 +17,24 @@
* ============================================================ */
#pragma once
#include <QQmlEngine>
#include <QQmlContext>
class QmlEngine : public QQmlEngine
#include "plugins.h"
class QmlPluginContext : public QQmlContext
{
Q_OBJECT
public:
explicit QmlEngine(QObject *parent = nullptr);
QString extensionName();
void setExtensionName(const QString &name);
QString extensionPath();
void setExtensionPath(const QString &path);
explicit QmlPluginContext(const Plugins::Plugin &plugin, QQmlEngine *engine, QObject *parent = nullptr);
QString pluginPath() const;
QString pluginName() const;
Plugins::Plugin plugin() const;
static QmlPluginContext *contextForObject(const QObject *object);
private:
QString m_extensionName;
QString m_extensionPath;
Plugins::Plugin m_plugin;
};
......@@ -31,15 +31,18 @@
#include "api/tabs/qmltab.h"
#include "webpage.h"
#include "qztools.h"
#include "qml/qmlengine.h"
#include "qml/qmlplugincontext.h"
#include <QDebug>
#include <QQuickWidget>
#include <QDialog>
#include <QVBoxLayout>
#include <QQmlEngine>
QmlPluginInterface::QmlPluginInterface()
: m_qmlReusableTab(new QmlTab())
{
// QQmlEngine::setContextForObject(m_qmlReusableTab, QmlPluginContext::contextForObject(this));
}