Porting Plasmoids to KF6
The Plasma API used to make desktop widgets (also known as "applets" or "plasmoids") is changing for the Plasma 6.0 release.
Changes Overview
Background Information
A plasmoid's base component is a C++ class called Applet
, which is a QObject
. Applets are contained in an Applet subclass called Containment
, which manages the lifecycle of all of its applets. On Plasma Desktop, each screen has one containment which manages its desktop area, and another for each panel on that desktop.
The root of the QML part of a plasmoid is a C++ QQuickItem
implementation called AppletInterface
. If a plasmoid is also a Containment
, then its base is a ContainmentInterface
, which is a subclass of AppletInterface
.
How it worked in Plasma 5
In Plasma 5, AppletInterface
exposed and wrapped most of the Applet
API, plus some QtQuick-related additions. The root QML object of each plasmoid could be any QML Item, and the AppletInterface
was accessed in QML via the Plasmoid
attached property or the plasmoid
(lowercase) context property.
The Applet
base instance was usually not accessible, unless the plasmoid offered a C++ Applet
plugin, in which case it was accessible via the property Plasmoid.nativeInterface
.
How it works in Plasma 6
In Plasma 6, AppletInterface
and Applet
have a more clear task separation. All the wrapper properties and methods from AppletInterface
to Applet
(and from ContainmentInterface
to Containment
) have been removed. AppletInterface
and ContainmentInterface
only offer QtQuick-specific API now.
The root QML object of a plasmoid is now required to be a PlasmoidItem
which is the QML name for AppletInterface
. If it is also a containment, it must be of type ContainmentItem
, which is the QML name for ContainmentInterface
. This approach is similar and consistent with the ApplicationWindow
type for QML-based applications.
The attached Plasmoid
object, as well the lowercase plasmoid
context property are now instances of the underlying Applet
(or Containment
for panels and desktops), keeping most of the API present in the Plasma 5 version of the attached property.
The new PlasmoidItem
root element--which is the QML frontend to the C++ AppletInterface
class--maintains the following properties, which were already there in Plasma 5:
compactRepresentation
fullRepresentation
preferredRepresentation
switchWidth
switchHeight
activationTogglesExpanded
toolTipMainText
toolTipSubText
toolTipTextFormat
toolTipItem
hideOnWindowDeactivate
In Plasma 6, a new plasmoid
property has been added, which points to the Applet
instance.
The new ContainmentItem
root element--which is the QML frontend to the C++ ContainmentInterface
class--maintains the following properties, which were already there in Plasma 5:
wallpaper
Methods already present in Plasma 5:
processMimeData
containmentAt
mapFromApplet
mapToApplet
adjustToAvailableScreenRegion
openContextMenu
In Plasma 6, a new method AppletInterface *itemFor(Plasma::Applet *applet)
has been added, which is used to get the AppletItem
instance of a given Applet
. NOTE: it may return null when invoked before componentComplete
of the ContainmentItem
is executed (FIXME).
Porting an existing plasmoid
The main thing to consider is adapting to the new subdivision of API between PlasmoidItem
and Plasmoid
. In general, you will need to take the following steps:
- Port the root
Item
of the plasmoid toPlasmoidItem
(orContainmentItem
, if the plasmoid is a containment). If the root item wasn't anItem
but rather something functional like aMouseArea
, that item should be made a child of the new rootPlasmoidItem
orContainmentItem
. - All the properties and methods mentioned above will have to be ported from the
Plasmoid
attached property (orplasmoid
context property) to be properties set on the rootPlasmoidItem
orContainmentItem
item. Other properties, such asicon
andtitle
can remain using thePlasmoid
attached property. - for plasmoids that offer a C++ plugin as an
Applet
subclass, the C++ part doesn't change. The QML part drops itsnativeInterface
intermediary property, so change properties likePlasmoid.nativeInterface.myProperty
toPlasmoid.myProperty
- Un-version your QML module imports. This is unnecessary in Qt6 and can cause bugs.
In addition, some changes to applets' metadata are needed. In Plasma 6, all plasmoids must use JSON metadata. You can convert your plasmoid's old-style .desktop file to the new-style .json file using the desktoptojson
command line utility. Beyond the automatic desktop-to-json metadata conversion, a few manual tweaks are also needed:
- Set
"X-Plasma-API-Minimum-Version": "6.0"
so that the system sees the plasmoid. Plasmoids without this key are assumed to only work with Plasma 5 and will not be mad available in the UI. - Remove the
X-Plasma-MainScript
entry. In Plasma 6,ui/main.qml
is always used as the entry point, so make sure that's the name of your "main" file. - Remove the
X-Plasma-API
entry.
If you automatically converted the metadata.json
from a metadata.desktop
, the KPlugin
section may still contain the ServiceTypes
key.
This needs to be replaced by a KPackageStructure
entry in the json's top level.
For example, the following ServiceTypes
section must be reworked:
{
"ServiceTypes": [
"Plasma/Applet"
]
}
into this KPackageStructure
definition:
{
"KPackageStructure": "Plasma/Applet"
}
Example porting of a minimal plasmoid
Qt5
import QtQuick 2.15
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.plasmoid 2.0
Item {
id: root
Plasmoid.toolTipMainText: i18n("This is %1", Plasmoid.title)
Plasmoid.fullRepresentation: Item { ... }
Plasmoid.compactRepresentation: Item { ... }
}
Qt6
import QtQuick 2.15
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.plasmoid
PlasmoidItem {
id: root
// toolTipMainText is a direct property of root, title is a property of Plasmoid attached property
toolTipMainText: i18n("This is %1", Plasmoid.title)
fullRepresentation: Item { ... }
compactRepresentation: Item { ... }
}
Components to replace with others of same functionality
Things moved to Kirigami
The following things have been replaced by their Kirigami counterparts:
Plasma 5 | Plasma 6 |
---|---|
PlasmaCore.ColorScope.colorGroup | Kirigami.Theme.colorSet |
PlasmaCore.ColorScope.inherit | Kirigami.Theme.inherit |
PlasmaCore.Theme.smallestFont | Kirigami.Theme.smallFont |
PlasmaCore.Theme.NormalColorGroup | Kirigami.Theme.Window |
PlasmaCore.Theme.anything else | Kirigami.Theme.the same thing |
PlasmaCore.Theme.mSize(font).height | Kirigami.Units.gridUnit |
PlasmaCore.Units.largeSpacing | Kirigami.Units.gridUnit |
PlasmaCore.Units.devicePixelRatio | 1 |
PlasmaCore.Units.roundtoIconSize(font height) | Kirigami.Units.iconSizes.sizeForLabels |
PlasmaCore.Units.roundtoIconSize(value) | Kirigami.Units.iconSizes.roundedIconSize(value) |
PlasmaCore.Units.anything else | Kirigami.Units.the same thing |
PlasmaCore.IconItem | Kirigami.Icon |
PlasmaExtras.Heading | Kirigami.Heading |
Note: remember to add import org.kde.kirigami as Kirigami
in
files that don't have it yet
New KSvg framework
Everything regarding SVG theming has moved from Plasma Framework to a new framework called KSvg.
The API is compatible, with the exception of the colorGroup:
property which
has been removed from KSvg because its functionality is now provided automatically.
The import needs to be changed to:
import org.kde.ksvg as KSvg
Plasma 5 | Plasma 6 |
---|---|
PlasmaCore.Svg | KSvg.Svg |
PlasmaCore.SvgItem | KSvg.SvgItem |
PlasmaCore.FrameSvgItem | KSvg.FrameSvgItem |
Also, even if the old syntax is still compatible, it's recommended to change:
KSvg.SvgItem {
svg: KSvg.Svg {
imagePath: "widgets/background"
}
}
To the more compact form:
KSvg.SvgItem {
imagePath: "widgets/background"
}
New Plasma5Support library
Some parts of Plasma Framework have moved to Plasma5Support, such as types for the Data Engines system.
Their new import is:
import org.kde.plasma.plasma5support as Plasma5Support
Plasma 5 | Plasma 6 |
---|---|
PlasmaCore.DataSource | Plasma5Support.DataSource |
PlasmaCore.DataModel | Plasma5Support.DataModel |
New Actions API
Plasmoids can add contextual actions, which appear in their headers (when used in the System Tray) and context menus.
In Plasma 5 these actions were defined using an imperative API in Component.onCompleted:
, like so:
Component.onCompleted: {
Plasmoid.clearActions()
Plasmoid.setAction("previous", i18nc("Play previous track", "Previous Track"),
Qt.application.layoutDirection === Qt.RightToLeft ? "media-skip-forward" : "media-skip-backward");
Plasmoid.action("previous").enabled = Qt.binding(() => root.canGoPrevious)
Plasmoid.action("previous").visible = Qt.binding(() => root.canControl)
Plasmoid.action("previous").priority = Plasmoid.LowPriorityAction
...
}
// Handling of actions then had to be done by adding "magic" functions:
function action_previous() {
serviceOp(mpris2Source.current, "Previous");
}
This has been replaced with a purely declarative API, in the form of a new QML type
called PlasmaCore.Action
and a contextualActions:
list property on the Plasmoid
item, where all the actions are declared with standard array syntax. The Plasma 6 version
of the above example can be rewritten as follows:
Plasmoid.contextualActions: [
PlasmaCore.Action {
text: i18nc("Play previous track", "Previous Track")
icon.name: Qt.application.layoutDirection === Qt.RightToLeft ? "media-skip-forward" : "media-skip-backward"
priority: Plasmoid.LowPriorityAction
visible: root.canControl
enabled: root.canGoPrevious
onTriggered: serviceOp(mpris2Source.current, "Previous")
},
...
]
This way, both property bindings and the code to be executed when the action is
activated are all defined inline in a declarative way inside the PlasmaCore.Action
definition.
PlasmaCore.SortFilterModel to KItemModels.KSortFilterProxyModel
The item SortFilterModel
from the import org.kde.plasma.core
has been removed.
Any usages of it should be ported to the KSortFilterProxyModel
component which offers
the same functionality. You can import it using org.kde.kitemmodels
.
There are some key differences. While SortFilterModel
has properties sortRole
and filterRole
that take strings as role names, KSortFilterProxyModel
has the same two properties accepting only integers as the actual role values.
Usages of role names must be ported to the properties sortRoleName
and filterRoleName
(sortRole
and filterRole
will automatically sync to the coresponding role number)
The filterRegExp
property becomes filterRegularExpression
which will have to be a RegExp()
type, such as filterRegularExpression: RegExp(".*foo.+")
.
Another change in usage is filterCallback
which becomes filterRowCallback
and filterColumnCallback
with a different API: the value
parameter is gone, replaced by the parent index, from which the data of any role number can be extracted.
Example usage:
KItemModels.KSortFilterProxyModel {
id: configDialogFilterModel
sourceModel: ...
filterRoleName: "name"
filterRowCallback: (sourceRow, sourceParent) => {
// filterRole will be the corresponding number for the role named the value set in filterRoleName
let value = sourceModel.data(sourceModel.index(sourceRow, 0, sourceParent), filterRole);
return value === "foo";
}
}
ExpandableListItem
- Rename property
contextualActionsModel
tocontextualActions
. - Properties
iconUsesPlasmaSVG
,contextMenu
andenabledActions
are gone. - Port
isEnabled
property to the standardenabled
one.
Configuration UI
If the plasmoid ships with a configuration UI this needs to be adjusted. The root item for a configuration page now must be one of the KCM components from the org.kde.kcmutils
import.
Available are:
SimpleKCM
: Use this for arbitrary content, e.g. a FormLayout or a column of controls. This will be the right thing for most applets.AbstractKCM
: Similar to SimpleKCM, but does not contain a ScrollView. Use this when you need control over where and how to use a ScrollView in the UI.ScrollViewKCM
: Use this when your configuration features a central ListView or similar view. Additional controls can be placed asheader
andfooter
of the control.GridViewKCM
: Use this when your configuration features a central GridView.
Example usage:
import QtQuick
import QtQuick.Controls
import org.kde.kirigami as Kirigami
import org.kde.kcmutils as KCM
KCM.SimpleKCM {
property var cfg_myConfigKey
Kirigami.FormLayout {
Button {
...
}
}
}