From 2750155af49dde56fc8e048c4b8594c90516cd78 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 23 Nov 2015 20:31:20 +0100 Subject: [PATCH 1/6] a minimal example of a custom shell --- examples/CMakeLists.txt | 1 + examples/shell/CMakeLists.txt | 21 ++++++++ examples/shell/customcorona.cpp | 88 +++++++++++++++++++++++++++++++++ examples/shell/customcorona.h | 42 ++++++++++++++++ examples/shell/main.cpp | 53 ++++++++++++++++++++ 5 files changed, 205 insertions(+) create mode 100644 examples/shell/CMakeLists.txt create mode 100644 examples/shell/customcorona.cpp create mode 100644 examples/shell/customcorona.h create mode 100644 examples/shell/main.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 38d5e823f..cc76a7f9b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(containments) add_subdirectory(wallpapers) add_subdirectory(dataengines) add_subdirectory(testcontainmentactionsplugin) +add_subdirectory(shell) diff --git a/examples/shell/CMakeLists.txt b/examples/shell/CMakeLists.txt new file mode 100644 index 000000000..7799d32ae --- /dev/null +++ b/examples/shell/CMakeLists.txt @@ -0,0 +1,21 @@ +set(exampleplasmashell-app_SRCS + customcorona.cpp + main.cpp +) + +add_executable(exampleplasmashell ${exampleplasmashell-app_SRCS}) + +target_link_libraries( + exampleplasmashell + Qt5::Widgets + Qt5::Quick + Qt5::Qml + KF5::I18n + KF5::XmlGui + KF5::PlasmaQuick + KF5::Plasma + KF5::DBusAddons + KF5::Notifications + KF5::QuickAddons +) + diff --git a/examples/shell/customcorona.cpp b/examples/shell/customcorona.cpp new file mode 100644 index 000000000..52c741d7e --- /dev/null +++ b/examples/shell/customcorona.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2015 Marco Martin + * + * 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 desktopFoundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 + */ + +#include "customcorona.h" +#include +#include +#include + +#include +#include +#include + +CustomCorona::CustomCorona(QObject *parent) + : Plasma::Corona(parent) +{ + KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Shell")); + package.setPath(QStringLiteral("org.kde.plasma.desktop")); + setKPackage(package); + + qmlRegisterUncreatableType("org.kde.plasma.shell", 2, 0, "Desktop", QStringLiteral("It is not possible to create objects of type Desktop")); + + m_view = new PlasmaQuick::ContainmentView(this); + m_view->setSource(QUrl::fromLocalFile(package.filePath("views", QStringLiteral("Desktop.qml")))); + m_view->show(); + + load(); +} + + + +QRect CustomCorona::screenGeometry(int id) const +{ + Q_UNUSED(id); + //TODO? + return QRect(); +} + +void CustomCorona::load() +{ + loadLayout(QStringLiteral("exampleplasmashell-appletsrc")); + + + bool desktopFound = false; + for (auto c : containments()) { + if (c->containmentType() == Plasma::Types::DesktopContainment) { + desktopFound = true; + break; + } + } + + if (!desktopFound) { + qDebug() << "Loading default layout"; + Plasma::Containment *c = createContainment(QStringLiteral("org.kde.desktopcontainment")); + c->createApplet("org.kde.plasma.analogclock"); + saveLayout(QStringLiteral("exampleplasmashell-appletsrc")); + } + + //don't let containments to be removed + for (auto c : containments()) { + if (c->containmentType() == Plasma::Types::DesktopContainment) { + m_view->setContainment(c); + QAction *removeAction = c->actions()->action(QStringLiteral("remove")); + if(removeAction) { + removeAction->deleteLater(); + } + break; + } + } +} + +#include "moc_customcorona.cpp" diff --git a/examples/shell/customcorona.h b/examples/shell/customcorona.h new file mode 100644 index 000000000..de00f751b --- /dev/null +++ b/examples/shell/customcorona.h @@ -0,0 +1,42 @@ +/* + * Copyright 2015 Marco Martin + * + * 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 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 + */ + +#ifndef CUSTOMCORONA_H +#define CUSTOMCORONA_H + +#include +#include "plasmaquick/containmentview.h" + +class CustomCorona : public Plasma::Corona +{ + Q_OBJECT + +public: + explicit CustomCorona(QObject * parent = 0); + QRect screenGeometry(int id) const; + +public Q_SLOTS: + void load(); + +private: + PlasmaQuick::ContainmentView *m_view; +}; + +#endif diff --git a/examples/shell/main.cpp b/examples/shell/main.cpp new file mode 100644 index 000000000..bf96fa4ff --- /dev/null +++ b/examples/shell/main.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Marco Martin + * + * 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 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 + */ + +#include +#include +#include + +#include +#include + +#include "customcorona.h" + +static const char version[] = "1.0"; + +int main(int argc, char **argv) +{ + QQuickWindow::setDefaultAlphaBuffer(true); + + QApplication app(argc, argv); + app.setApplicationVersion(version); + app.setOrganizationDomain(QStringLiteral("kde.org")); + + KDBusService service(KDBusService::Unique); + + QCommandLineParser parser; + parser.setApplicationDescription(i18n("Plasma Example shell")); + parser.addVersionOption(); + parser.addHelpOption(); + parser.process(app); + + CustomCorona *corona = new CustomCorona(); + + const int ret = app.exec(); + delete corona; + return ret; +} From ff83c3ac994bbaff9ff372982406eb477c6ae194 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Nov 2015 10:58:05 +0100 Subject: [PATCH 2/6] install some plasmaquick headers --- examples/shell/customcorona.h | 4 ++-- src/plasmaquick/CMakeLists.txt | 23 +++++++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/examples/shell/customcorona.h b/examples/shell/customcorona.h index de00f751b..5a11067ea 100644 --- a/examples/shell/customcorona.h +++ b/examples/shell/customcorona.h @@ -21,8 +21,8 @@ #ifndef CUSTOMCORONA_H #define CUSTOMCORONA_H -#include -#include "plasmaquick/containmentview.h" +#include +#include class CustomCorona : public Plasma::Corona { diff --git a/src/plasmaquick/CMakeLists.txt b/src/plasmaquick/CMakeLists.txt index 5c0a7b38d..901a19fc5 100644 --- a/src/plasmaquick/CMakeLists.txt +++ b/src/plasmaquick/CMakeLists.txt @@ -68,18 +68,29 @@ generate_export_header(KF5PlasmaQuick BASE_NAME PlasmaQuick) set(plasmaquick_LIB_INCLUDES ${CMAKE_CURRENT_BINARY_DIR}/plasmaquick_export.h appletquickitem.h - view.h + containmentview.h configview.h configmodel.h + dialog.h packageurlinterceptor.h ) -#install(FILES ${plasmaquick_LIB_INCLUDES} -# DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/plasmaquick COMPONENT Devel) +ecm_generate_headers(PlasmaQuick_CamelCase_HEADERS + HEADER_NAMES + AppletQuickItem + ContainmentView + ConfigView + ConfigModel + Dialog + REQUIRED_HEADERS plasmaquick_LIB_INCLUDES + PREFIX PlasmaQuick +) -#install(DIRECTORY -# includes/PlasmaQuick -# DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KDE COMPONENT Devel) +install(FILES ${plasmaquick_LIB_INCLUDES} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/plasmaquick COMPONENT Devel) + +install(FILES ${PlasmaQuick_CamelCase_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/PlasmaQuick COMPONENT Devel) set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5PlasmaQuick") From 4e143813b477b8058c734819dfddfb3155b8769f Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Nov 2015 12:15:11 +0100 Subject: [PATCH 3/6] example without a wallpaper --- examples/shell/customcorona.cpp | 3 +++ src/plasma/data/servicetypes/plasma-applet.desktop | 3 +++ 2 files changed, 6 insertions(+) diff --git a/examples/shell/customcorona.cpp b/examples/shell/customcorona.cpp index 52c741d7e..290278530 100644 --- a/examples/shell/customcorona.cpp +++ b/examples/shell/customcorona.cpp @@ -31,6 +31,7 @@ CustomCorona::CustomCorona(QObject *parent) : Plasma::Corona(parent) { KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Shell")); + //applications that want to load a plasma scene would have to load their own shell.. TODO: have a simple shell in plasma-framework for this purpose? package.setPath(QStringLiteral("org.kde.plasma.desktop")); setKPackage(package); @@ -75,6 +76,8 @@ void CustomCorona::load() //don't let containments to be removed for (auto c : containments()) { if (c->containmentType() == Plasma::Types::DesktopContainment) { + //example of a shell without a wallpaper + c->setWallpaper("null"); m_view->setContainment(c); QAction *removeAction = c->actions()->action(QStringLiteral("remove")); if(removeAction) { diff --git a/src/plasma/data/servicetypes/plasma-applet.desktop b/src/plasma/data/servicetypes/plasma-applet.desktop index 9898da139..540bbeece 100644 --- a/src/plasma/data/servicetypes/plasma-applet.desktop +++ b/src/plasma/data/servicetypes/plasma-applet.desktop @@ -66,6 +66,9 @@ Type=QString [PropertyDef::X-Plasma-DBusActivationService] Type=QString +[PropertyDef::X-Plasma-ParentApp] +Type=QString + [PropertyDef::X-Plasma-Provides] Type=QStringList From 24150a39328bf289ed21aeaf72bc0481b6dbc95b Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 26 Nov 2015 18:15:00 +0100 Subject: [PATCH 4/6] make the example containment REALLY simple remove all the fluff, make it the least code possible for a base containment to work --- .../contents/ui/PlasmoidContainer.qml | 27 +++ .../testcontainment/contents/ui/main.qml | 194 ++---------------- 2 files changed, 41 insertions(+), 180 deletions(-) create mode 100644 examples/containments/testcontainment/contents/ui/PlasmoidContainer.qml diff --git a/examples/containments/testcontainment/contents/ui/PlasmoidContainer.qml b/examples/containments/testcontainment/contents/ui/PlasmoidContainer.qml new file mode 100644 index 000000000..e75bc898e --- /dev/null +++ b/examples/containments/testcontainment/contents/ui/PlasmoidContainer.qml @@ -0,0 +1,27 @@ +/* + * Copyright 2015 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.1 + +Item { + id: plasmoidContainer + width: 300 + height: 300 + property Item applet +} diff --git a/examples/containments/testcontainment/contents/ui/main.qml b/examples/containments/testcontainment/contents/ui/main.qml index 889afa3fd..6e45c6fd5 100644 --- a/examples/containments/testcontainment/contents/ui/main.qml +++ b/examples/containments/testcontainment/contents/ui/main.qml @@ -20,6 +20,7 @@ import QtQuick 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.plasmoid 2.0 Item { id: root @@ -28,188 +29,21 @@ Item { property Item toolBox - Connections { - target: plasmoid - onAppletAdded: { - var container = appletContainerComponent.createObject(root) - container.visible = true - print("Applet added: " + applet) - applet.parent = container - container.applet = applet - applet.anchors.fill= applet.parent - applet.visible = true - } + function addApplet(applet, x, y) { + var component = Qt.createComponent("PlasmoidContainer.qml") + var plasmoidContainer = component.createObject(root, {"x": x, "y": y}); + + plasmoidContainer.parent = root; + plasmoidContainer.applet = applet + applet.parent = plasmoidContainer + applet.anchors.fill = plasmoidContainer + applet.visible = true + plasmoidContainer.visible = true + } - Component { - id: appletContainerComponent - Item { - id: frameParent - x: 50 - y: 50 - - property int small: 90 - property int large: 400 - - width: large + frame.margins.left + frame.margins.right - height: large + frame.margins.top + frame.margins.bottom - - property alias applet: appletContainer.children - onAppletChanged: { - if (appletContainer.children.length == 0) { - killAnim.running = true - } - } - PlasmaCore.FrameSvgItem { - id: frame - anchors.fill: parent - - property int tm: 0 - property int lm: 0 - - imagePath: applet.length > 0 && applet[0].backgroundHints == 0 ? "" : "widgets/background" - - onImagePathChanged: { - // Reposition applet so it fits into the frame - if (imagePath == "") { - frameParent.x = frameParent.x + lm; - frameParent.y = frameParent.y + tm; - } else { - // Cache values, so we can subtract them when the background is removed - frame.lm = frame.margins.left; - frame.tm = frame.margins.top; - - frameParent.x = frameParent.x - frame.margins.left; - frameParent.y = frameParent.y - frame.margins.top; - } - } - MouseArea { - id: mouseArea - - property real dx: 0 - property real dy: 0 - property real startX - property real startY - - anchors.fill: parent - drag.target: frameParent - onClicked: { - var s = (frameParent.width == frameParent.large) ? frameParent.small : frameParent.large; - frameParent.height = s - frameParent.width = s - } - onPressed: { - speedSampleTimer.running = true - mouseArea.startX = mouse.x - mouseArea.startY = mouse.y - speedSampleTimer.lastFrameParentX = frameParent.x - speedSampleTimer.lastFrameParentY = frameParent.y - } - onPositionChanged: { - //mouseArea.dx = mouse.x - mouseArea.startX - //mouseArea.dy = mouse.y - mouseArea.startY - dxAnim.running = false - dyAnim.running = false - } - onReleased: { - speedSampleTimer.running = false - dxAnim.running = true - dyAnim.running = true - } - Timer { - id: speedSampleTimer - interval: 40 - repeat: true - property real lastFrameParentX - property real lastFrameParentY - onTriggered: { - mouseArea.dx = frameParent.x - lastFrameParentX - mouseArea.dy = frameParent.y - lastFrameParentY - lastFrameParentX = frameParent.x - lastFrameParentY = frameParent.y - dxAnim.running = true - dyAnim.running = true - } - } - } - - Item { - id: appletContainer - anchors { - fill: parent - leftMargin: frame.margins.left - rightMargin: parent.margins.right - topMargin: parent.margins.top - bottomMargin: parent.margins.bottom - } - } - - PlasmaComponents.BusyIndicator { - id: busyIndicator - z: 1000 - visible: applet.length > 0 && applet[0].busy - running: visible - anchors.centerIn: parent - } - SequentialAnimation { - id: killAnim - NumberAnimation { - target: frame - properties: "scale" - to: 0 - duration: units.longDuration - } - ScriptAction { script: frame.destroy()} - } - } - ShaderEffect { - id: wobbleShader - anchors.fill: frame - property variant source: ShaderEffectSource { - hideSource: true - sourceItem: frame - } - - property int fadeDuration: 150 - property real time: 0 - property real dx: mouseArea.dx - property real dy: mouseArea.dy - property real startX: mouseArea.startX/mouseArea.width - property real startY: mouseArea.startY/mouseArea.height - - NumberAnimation on dx { id: dxAnim; to: 0; duration: units.longDuration; easing.type: Easing.OutElastic } - NumberAnimation on dy { id: dyAnim; to: 0; duration: units.longDuration; easing.type: Easing.OutElastic } - //! [fragment] - fragmentShader: { - "uniform lowp float qt_Opacity;" + - "uniform highp float dx;" + - "uniform highp float dy;" + - "uniform highp float startX;" + - "uniform highp float startY;" + - "uniform sampler2D source;" + - "varying highp vec2 qt_TexCoord0;" + - "void main() {" + - - " highp vec2 tmp = 1.0 / (1.0 + abs(qt_TexCoord0 - vec2(startX, startY)));" + - " vec2 wave = qt_TexCoord0 - vec2(dx, dy) * 0.0025 * tmp.xx * tmp.yy;" + - " gl_FragColor = texture2D(source, wave);" + - "}" - } - //! [fragment] - } - } - } - PlasmaCore.IconItem { - width: 24 - height: 24 - source: "list-add" - MouseArea { - anchors.fill: parent - onClicked: { - print("Add widgets ..."); - plasmoid.action("add widgets").trigger(); - } - } + Containment.onAppletAdded: { + addApplet(applet, x, y); } Component.onCompleted: { From 088210363cf12da3ca3d13258f637889173c6f37 Mon Sep 17 00:00:00 2001 From: Olivier CHURLAUD Date: Fri, 27 Nov 2015 22:52:52 +0100 Subject: [PATCH 5/6] Header of plasmaquick_export.h is in plasmaquick folder. Maybe the same should be done to other files that refer to it. --- src/plasmaquick/containmentview.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plasmaquick/containmentview.h b/src/plasmaquick/containmentview.h index 3873a1ada..4bdc1dee3 100644 --- a/src/plasmaquick/containmentview.h +++ b/src/plasmaquick/containmentview.h @@ -21,7 +21,7 @@ #include -#include +#include #include "plasma/corona.h" #include "plasma/containment.h" From 2751b72835f1b6fdbca08846076bd5a8e6d2bb41 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 1 Dec 2015 17:39:05 +0100 Subject: [PATCH 6/6] mechanism for coronas to load plasmoids from custom folders applications such as Amarok can look for their plasmoids in their own data dir in order to not have all mixed up --- src/plasma/pluginloader.cpp | 21 +++++++++++++++++++-- src/plasma/pluginloader.h | 25 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/plasma/pluginloader.cpp b/src/plasma/pluginloader.cpp index bfa5deeec..d7fe7a890 100644 --- a/src/plasma/pluginloader.cpp +++ b/src/plasma/pluginloader.cpp @@ -56,7 +56,8 @@ class PluginLoaderPrivate public: PluginLoaderPrivate() : isDefaultLoader(false), - packageRE("[^a-zA-Z0-9\\-_]") + plasmoidsDataDir(PLASMA_RELATIVE_DATA_INSTALL_DIR), + packageRE(QLatin1String("[^a-zA-Z0-9\\-_]")) { KPackage::PackageLoader::self()->addKnownPackageStructure("Plasma/Applet", new PlasmoidPackage()); KPackage::PackageLoader::self()->addKnownPackageStructure("Plasma/DataEngine", new DataEnginePackage()); @@ -77,6 +78,7 @@ public: static QString s_plasmoidsPluginDir; static QString s_servicesPluginDir; static QString s_containmentActionsPluginDir; + QString plasmoidsDataDir; QRegExp packageRE; }; @@ -228,6 +230,7 @@ Applet *PluginLoader::loadApplet(const QString &name, uint appletId, const QVari } } + p.setDefaultPackageRoot(d->plasmoidsDataDir + "/plasmoids/"); // backwards compatibility: search in the root plugins directory // TODO: remove when Plasma 5.4 is released { @@ -270,6 +273,16 @@ Applet *PluginLoader::loadApplet(const QString &name, uint appletId, const QVari return applet; } +void PluginLoader::setAppletsDataDirectory(const QString &dir) +{ + d->plasmoidsDataDir = dir; +} + +QString PluginLoader::appletsDataDirectory() const +{ + return d->plasmoidsDataDir; +} + DataEngine *PluginLoader::loadDataEngine(const QString &name) { DataEngine *engine = d->isDefaultLoader ? 0 : internalLoadDataEngine(name); @@ -525,7 +538,11 @@ Package PluginLoader::loadPackage(const QString &packageFormat, const QString &s structure = new PackageStructure(); structure->d->internalStructure = internalStructure; d->structures.insert(hashkey, structure); - return Package(structure); + Package p(structure); + if (packageFormat == "Plasma/Applet") { + p.setDefaultPackageRoot(d->plasmoidsDataDir + "/plasmoids/"); + } + return p; } } diff --git a/src/plasma/pluginloader.h b/src/plasma/pluginloader.h index 76549c6bd..90a7fbf01 100644 --- a/src/plasma/pluginloader.h +++ b/src/plasma/pluginloader.h @@ -66,6 +66,31 @@ public: Applet *loadApplet(const QString &name, uint appletId = 0, const QVariantList &args = QVariantList()); + /** + * Sets the data directory for plasmoids. + * By default plasmoid packages will be loaded from the + * "plasma" data directory, usually under the "share" + * subdirectory of the install prefix. + * An application that uses a custom plasma shell + * can have its private plasmoids + * in its own data directory, not having them mixeed + * with the ones from plasma desktop. + * It takes effect only in applets loaded after setting + * this, so use it at the application startup. + * + * @param dir data directory name + * @since 5.18 + */ + void setAppletsDataDirectory(const QString &dir); + + /** + * @return the custom applets data directory. + * Default is "plasma" + * + * @since 5.18 + */ + QString appletsDataDirectory() const; + /** * Load a DataEngine plugin. *