Plasma Widget Tutorial
This tutorial has been upstreamed to the KDE Documentation with improvements like using the newer PlasmaComponents 3.0
and QtQuick.Controls 2.0
in your widget. This older tutorial still uses PlasmaComponents 2.0
and QtQuick.Controls 1.0
. This tutorial is out of date.
Default Widgets
đź–‰Learn By Example
You can learn by example by reading the default widgets located at:
/usr/share/plasma/plasmoids/
You can fork an existing widget by copying the widget to where the downloaded widgets are installed to:
~/.local/share/plasma/plasmoids/
To properly fork the widget, rename the new folder, then edit the X-KDE-PluginInfo-Name
in the metadata.desktop
file to match the new folder name. You will also want to edit the Name=
in the metadata.desktop
file as well so you can tell it apart from the widget you forked it from. You should delete all the translated Name[fr]=
lines as well. Delete the metadata.json
if it exists (or edit it as well).
Finally, run plasmawindowed
to quickly test the newly forked widget.
Setup
đź–‰Folder Structure
To start a new widget from scratch, first create a folder for your new widget somewhere in you coding directory (eg: ~/Code/plasmoid-helloworld
).
Inside it create another folder called package
. Everything inside the package
folder will be what we eventually install to ~/.local/share/plasma/plasmoids/com.github.zren.helloworld/
. Eventually we will zip the contents of package
and share them online. We can keep text editor files, build scripts, screenshots, etc outside the package
directory.
Inside the package folder will be a metadata.desktop
file which is basically a Linux .ini
file. This file will describe the name of the widget, the category it’s in, and various other plasma specific keys like the main QML file.
Inside contents
, we will create the ui
and config
folders. ui
is the folder which should contain your layout files like the main.qml
and the configGeneral.qml
. configGeneral.qml
is the layout of the first tab in the widget’s configuration window.
Inside the config
folder we have the main.xml
which contains the schema of all our serialized configuration keys+values. The config.qml
is used to define the tabs in the configuration window. Each tab will open a QML layout file (like ui/configGeneral.qml
).
Note that you don’t need the 3 config files. You can get away with just the main.qml
and metadata.desktop
for a barebones widget.
~/Code/plasmoid-helloworld/
package/
contents/
config/
config.qml
main.xml
ui/
configGeneral.qml
main.qml
metadata.desktop
metadata.desktop
Inside the metadata.desktop
file we need to set the Name
of the widget. The Type
should be Service
since the .desktop
file is not an app launcher and we don’t want this to appear in the app menu.
Icon
is the icon name associated with the widget. You can search for icon names in the /usr/share/icon
folder. You can also look for an icon name by right clicking your app launcher widget then editing the icon in it’s settings. It uses a searchable interface and lists them by category. Plasma’s SDK also has the Cuttlefish app (screenshot) which you can install with sudo apt install plasma-sdk
.
X-KDE-PluginInfo-Name
needs to be a unique name, since it’s used for the folder name it’s installed into. You could use com.github.zren.helloworld
if you’re on github, or use org.kde.plasma.helloworld
if you’re planning on contributing the widget to KDE.
Widgets installed by the user (without root) like when you “Install New Widgets” will be installed to ~/.local/share/plasma/plasmoids/
(which may not yet exist). The default widgets shipped with KDE are installed to /usr/share/plasma/plasmoids/
.
X-KDE-PluginInfo-Category
is the category the widget can be filtered with in the widget list. A list of category names can be found here.
X-KDE-ServiceTypes
, X-Plasma-API
, and X-Plasma-MainScript
are also needed to just define that this package is a plasma widget, and where it’s entry point is.
For more, read the Getting Started tutorial on the KDE wiki.
contents/ui/main.qml
This is the entry point. Various properties are available to be set. You should know that widgets have several ways of being represented.
- You can have a widget in the panel, which is just an icon that will show a popup window when clicked.
- You can also have it on the desktop as a desktop widget which can be resized by the user. As a desktop widget it can switch between the “icon view” when smaller (which opens a popup), and directly showing the contents of the popup on the desktop when there’s enough room.
- You can also have the widget inside another widget (a containment) like the system tray or the panel itself.
- The widget can also be run like an application in it’s own window (Calculator).
plasmoid.location
and plasmoid.formFactor
can tell you how the widget is placed. plasmoid
is a global variable which is defined when you import org.kde.plasma.plasmoid 2.0
. Read more below.
Plasmoid.compactRepresentation
(with a capital) and Plasmoid.fullRepresentation
are used to define the layout of the small “icon” view and the full “popup” view. These are both properties of the main Item
. If neither are set, by default the main Item
is the full representation.
If you change the compact representation, you will need to use a MouseArea
to toggle the plasmoid.expanded
property. See the DefaultCompactRepresentation.qml
for an example.
Layout.preferredWidth
can be used to define the default width of a panel widget, or the size of the popup window (unless it is in the system tray). The system tray has a fixed hardcoded size for it’s popups. Layout.preferredWidth
can also define the width of the compact “icon” view in the horizontal panel, not just the full “popup” width. Note that the Layout.preferredWidth
/Layout.preferredHeight
of the Plasmoid.compactRepresentation
will automatically scale to the thickness of the panel depending on if it’s a vertical or horizontal panel.
Layout.minimumWidth
can be used to define the minimum size for a desktop widget / popup.
width
/height
(not Layout.__
) can be used to define the default size of a desktop widget. Desktop widgets currently ignore Layout.preferredWidth
when calculating the default size.
You can set the tooltip contents and various other things in the main.qml
.
Examples of main.qml
- Various examples in the Getting Started tutorial on the KDE wiki.
- colorpicker/package/contents/ui/main.qml
- fifteenPuzzle/package/contents/ui/main.qml
To show the text in the panel rather than in a popup:
To set the popup size:
Testing
đź–‰plasmawindowed
There are 3 ways to test a widget.
plasmawindowed
can be used if the widget is installed to:
~/.local/share/plasma/plasmoids
It will remember any changes you make to the config as this is the same command used for “Windowed widgets” like the “Calculator” app. It has limited features for displaying the widget, but the command should be preinstalled.plasmoidviewer
, explained further down, can display a widget as a desktop widget, or a panel widget. You can also test a widget that is not yet installed. You will need to first install theplasma-sdk
package to use it.- Install the widget and add it to your panel. Restarting plasma every time using:
plasmashell --replace
I only recommend this testing method for a final test as it takes a few seconds for the panel to load.
plasmoidviewer
With metadata.desktop
and main.qml
, we now have enough to test our widget. If you haven’t yet, install the plasma-sdk
package with sudo apt install plasma-sdk
.
Test as Desktop Widget
Note that --location=desktop
is used for the desktop wallpaper, not desktop widgets. Desktop widgets use --location=floating
.
Test as Horizontal Panel Widget
If we set plasmoidviewer
’s plasmoid.formFactor
to be horizontal
and plasmoid.location
to the topedge
or bottomedge
, we can test a widget focusing in the panel.
Testing DPI Scaling
By setting the QT_SCALE_FACTOR=2
we can double the DPI value from 96
to 192
just for the plasmoidviewer
window. This is great for testing if your code will support a HiDPI screen.
If you’re testing a very high DPI, you’ll probably find the default plasmoidviewer
window is too small to show the widget, so we’ll set the size and position of the window. Note that the window will go maximized if you set a size larger than you screen has available.
Enable logging
By default in Qt 5.9, console.log()
, which used to write a string to stdout (the Terminal output), is hidden by default. In order to reenable it, we need to set [Rules] qml.debug=true
in ~/.config/QtProject/qtlogging.ini
. You can easily set it by running this kwriteconfig5
command:
Qml
đź–‰Quick Intro
This is a quick intro to QML. If you’re comfortable with it, skip to the next section.
The official QML tutorial can be found in the QML Documentation.
Item
An Item is a simple object. It can have children as well. Item’s have a default width and height of 0px, and will not grow to fit their contents. So unlike the HTML box model, you’ll need to use layouts mentioned below.
Rectangle
If we want to draw a colored rectangle, we can easily do so with Rectangle. For other properties of the Rectangle, like border color and width, read it’s page in the QML Documentation.
Items are 0px wide by default
By default, an Item will not expand to fit it’s contents. Nor will it expand to fit the width of it’s parent (like a <div>
in HTML).
In the this example, only the Teal Rectangle will be visible, since the Green Rectangle has the default width of 0px and height of 0px. The Teal Rectangle is only visible since the root item in a widget’s main.qml
has a default size which will be explained later.
anchors.fill
In this second example, we make the Green Rectangle resize to the parent item, the Teal Rectangle. This will completely cover the Teal Rectangle so only the Green Rectangle will be visible.
anchors.bottom
In this third example, we anchor the Green Rectangle to the bottom right, and make it half the width & height of the Teal rectangle. So we end up with a rectangle which is 3/4 teal and 1/4 green.
Other ways to use anchors
properties can be read in the QML Documentation page on Positioning with Anchors and the Item.anchors
property group.
ColumnLayout
If you want to stack a number of items on top of each other, you should use a ColumnLayout
.
Labels (which are just fancy Text items which follow Plasma’s colors) have a default font size, which means they have their own default height. So they will be stacked on top of each other.
Note that if the ColumnLayout
is taller than it’s contents, the children will have spacing between them.
Layout.fillWidth: true
If you want an item to scale to the parent’s width, you have the option of setting it to be the same width as the parent (which doesn’t work in a Layout). You can also try anchoring to the left and right (which does work).
Within a Layout however, the proper way to do so is to use the special property attached to the contents of a Layout, Layout.fillWidth
. Setting it to true
will make the item scale to fill up the empty space.
The other Layout related properties can be read here.
Layout.fillHeight: true
If you want one item (or several) in a Layout to expand to take up the unused space, you can use Layout.fillHeight: true
.
Spacing between items in a Layout
In the last screenshot you might have noticed how there is still spacing between the items. That’s because the default ColumnLayout.spacing property is set to 5
. Assigning it to 0
will remove the extra whitespace.
Other Layouts
There’s also RowLayout and GridLayout. Lastly there’s Flow which will treat it’s contents as if they all had the CSS display: inline-block
.
Plasma's QML API
đź–‰Intro
KDE Frameworks ships with a number of useful extensions to Qt’s QML. The API documentation is a good start if you need to know what a specific property does. If you want to browse any of the sources easier, it’s also mirrored on GitHub.
PlasmaComponents.Label
QML ships with a Text type, but Plasma extends it with Label.qml
which assigns a number of defaults. One thing is it sets the text color to follow the panel’s color scheme. For the specifics, you can read the Label.qml
source code.
Heading, Paragraph
To be consistent with elsewhere in Plasma, Plasma ships with a couple different Label/Text types with preset default sizes. You will need to import PlasmaExtras
to use them.
Heading.qml
Various Font Size levels, Wraps withLayout.fillWidth: true
Paragraph.qml
Justified Alignment, Wraps withLayout.fillWidth: true
PlasmaComponents Controls
QML ships with various controls, like CheckBox, RadioButton, ComboBox (DropDown Menu), SpinBox, Slider, TextField, TextArea, Button, ToolButton. Plasma extends these controls to style them using the SVGs from the Plasma Theme. It also assigns a number of default settings like setting the text color to follow the panel’s color scheme. For Plasma’s specific changes, you can read the QML source code for each control in:
plasma-framework
/src/declarativeimports/plasmacomponents/qml/
CheckBox - Toggle
For a simple toggle, QML ships with CheckBox. For Plasma’s specific changes, you can read the QML source code at:
SpinBox, Slider - Numbers
To control Integer or Real numbers, QML ships with SpinBox and Slider. For Plasma’s specific changes, you can read the QML source code for each:
SpinBox.qml
(not skinned)Slider.qml
TextField, TextArea - Input
To enter text, QML ships with TextField and TextArea. For Plasma’s specific changes, you can read the QML source code for each:
PlasmaExtras.ScrollArea
To add a scrollbar to manage overflow, QML ships with ScrollView. For Plasma’s specific changes, you can read the QML source code at:
I recommend you set the ScrollArea’s contentItem.width
to viewport.width
.
Configuration
đź–‰Configuration Intro
Every widget by default has a configure action when you right click the widget called MyWidget Settings...
. By default it will contain a form to set a global shortcut to activate your widget.
contents/config/main.xml
main.xml
is where you define the properties that will be serialized into ~/.config/plasma-org.kde.plasma.desktop-appletsrc
. All properties will be accesible with plasmoid.configuration.variableName
reguardless of was group it’s in.
KConfig has a variety of data types:
Int
for an Integer numberDouble
for a double precision floating point number (Real)String
for a string of characters to represent textColor
for a hexidecimal color. The color defaults to#000000
(black) if the default is left empty.Path
is a string that is specially treated as a file-path. In particular paths in the home directory are prefixed with$HOME
when being stored in the configuration file.StringList
for a comma seperated list of Strings
I’ve listed the more common usecases. More can be found on the wiki:
https://techbase.kde.org/Development/Tutorials/Using_KConfig_XT
I personally don’t recommend using Color
if you want the default color from the color scheme (eg: theme.textColor
). I would instead suggest using a String
that defaults to empty. You can then use the following in the QML:
contents/config/config.qml
config.qml
is where we define the tabs in the configuration window.
We import the ConfigModel
and ConfigCategory
, and define the tab name, icon, and qml file that will be loaded.
contents/ui/configGeneral.qml
configGeneral.qml
is where we can place all the checkboxes and textboxes.
Please note that your should not use PlasmaComponents.*
controls in the config window, as those are styled and colored for the panel. The normal QtQuick.Controls
are styled using your application window style + colors.
Kirigami.FormLayout
is used to layout the controls in the center of the page. The Kirigami.FormData.label
attached property is used to place labels in front of the controls. CheckBoxes have their own labels, so you don’t need to set a Kirigami label. You can use it for CheckBoxes as a section seperator though. You do not need to set a Kirigami label.
configPage.cfg_variableName
By default, all values are copied to the top level Item
of the file prefixed with cfg_
like page.cfg_variableName
. This is so the user can hit discard or apply the changes. You will need to define each cfg_
property so you can bind the value with a QML control.
Note that you can use a property alias to a control’s property like checkBox.checked
or textField.text
.
CheckBox - Boolean
A CheckBox is used for boolean on/off values. See the Visual Design Group’s tips on using CheckBoxes.
SpinBox - Integer
A SpinBox is used for numbers.
If you want decimal places, a QtQuick.Controls 1.0
SpinBox is a little easier to use than the QtQuick.Controls 2.0
version. QtQuickControls1
has a SpinBox.decimals
to easily switch from an Integer decimals: 0
to decimals: 3
to represent a Real number (the Double
data type).
SpinBox - Double/Real
If you want decimal places, a QtQuick.Controls 1.0
SpinBox is a little easier to use than the QtQuick.Controls 2.0
version. QtControls1
has a SpinBox.decimals
property to easily switch from an Integer decimals: 0
to decimals: 3
to represent a Real number (the Double
config data type).
TextField - String/Text
A TextField is used for a single line of text. It can be used as a base for many other data types as well. You will also want to look at the base TextInput for more properties.
TextArea - Multi-Line String/Text
A TextArea is used for paragraphs of text. You will also want to look at the base TextEdit for more properties.
Assigning to plasmoid.configuration.varName
You can also assign directly to plasmoid.configuration.variableName
if necessary in the configruation window or anywhere else in your widget. If you do this in the configuration page, you will skip the “apply” process and the property will be applied right away. I leave this up to the reader wither this is a pro or con.
Configuration Examples
To learn by example, we can look at a couple widgets:
No-Apply Control Library
I have written a few files that apply the above pattern of skipping “Apply” and updating right after you change the value. It still uses the QtQuick.Controls 1.0
controls at the moment however.
- ConfigCheckBox.qml for on/off booleans values.
- ConfigSpinBox.qml for Integer or Real numbers.
- ConfigString.qml for a single line of text.
- ConfigColor.qml for use with a
String
orColor
config data type. If you use use aString
data type, you can treat an empty string as a certain color theme color. Eg:
- ConfigIcon.qml based on the Application Launcher icon selector.
- ConfigStringList.qml Instead of a TextField, it uses a TextArea using a new line as the seperator.
- ConfigComboBox.qml is useful for creating enums using the
String
config data type. KConfig comes with a enum datatype as well, but you have to use hardcoded integers (with comments) in your QML code, rather than using strings.- ConfigFontFamily.qml inherits
ConfigComboBox.qml
and is populated with all available fonts.
- ConfigFontFamily.qml inherits
- ConfigRadioButtonGroup.qml uses a similar model as
ConfigComboBox.qml
but displays the enum values differently. - ConfigTextAlign.qml for use with an
Int
config data type. It has your typical 4 buttons for left/center/right/justify alignment. It serializes theText.AlignHCenter
enum, which is an Integer.- ConfigTextFormat.qml is used to config bold, italic, underline, and embeds the text alignment. For use with 3
Bool
config keys and 1Int
config key (used for the embededConfigTextAlign.qml
).
- ConfigTextFormat.qml is used to config bold, italic, underline, and embeds the text alignment. For use with 3
Translations/i18n
đź–‰ki18n
Ki18n (KDE internationalization) is the translation library for KDE. It has a programmer’s guide which you can read, but we’ll cover the basics here.
i18n()
Translated strings need to be wrapped in the i18n(...)
function. Note that single quotes i18n('Test')
will be ignored by the tool that parses your code for all the translation strings. Always use double quotes i18n("Test")
.
Variables in i18n()
The i18n(...)
is an overloaded function which allows you to pass values into the translation i18n(format, variable1, variable2)
. Just place %1
where you want the first variable to be substitued, and %2
where the second should go.
Plural in i18n()
In English, a translated sentence is different when there’s just 1 item from when there is 2 or more items. i18np(...)
can be used in such a situation.
An example from the Ki18n docs is:
Using i18np(...)
can improve our previous example. When unreadEmailCount
was 1
, the tooltip would have read "1 unread emails"
.
Translation Folder Structure
After we’ve wrapped all the messages in our code with i18n(...)
calls, we then need to extract all the messages for our translators into a template.pot
file which they can then create a fr.po
for their French translations.
We’ll place the template.pot
file under a translate
folder inside the bundled package so that our users can easily translate our widget when they go poking into our code.
We’ll also create a merge.sh
script which will extract the messages from our code into a template.pot
, then update the translated fr.po
file with any changes.
Lastly, we’ll make a build.sh
script to convert the fr.po
text files into the binary .mo
files which are needed for KDE to recognize the translations.
The latest copy of my merge.sh
and build.sh
can be found in any of my widgets:
A working example can be seen in my TiledMenu widget:
~/Code/plasmoid-helloworld/
package/
contents/
...
translate/
fr.po
template.pot
build.sh
merge.sh
metadata.desktop
After running build.sh
we should end up with:
~/Code/plasmoid-helloworld/
package/
contents/
locale/
fr/
LC_MESSAGES/
plasma_applet_com.github.zren.helloworld.mo
...
Install GetText
After we’ve wrapped all the messages in our code with i18n(...)
calls, we then need to extract all the messages for our translators into a template.pot
file.
To do this, we need to install the gettext
package.
Generating template.pot
First thing we need to do in our merge.sh
script, is list all files we wish to get translated in our widgets code.
The latest copy of my complete
merge.sh
script can be found here.
DIR
is the directory (absolute path to package/translate/
) since we may run the merge script from another directory.
We use kreadconfig5
to grab the widget’s namespace (com.github.zren.helloworld
) and store it in plasmoidName
. We then remove the beginning of the namespace so we are left with helloworld
and store that in widgetName
. We also grab the website which a link to the GitHub repo for use as the bugAddress
.
After validating that plasmoidName
is not an empty string with bash’s [ -z "$plasmoidName" ]
operator, we then list all .qml
and .js
files using find
and store the results of the command in a temporary infiles.list
file.
Then we generate a template.pot.new
using the xgettext
command. After generating it, we use sed
to replace a few placeholder strings.
translate/merge.sh
Updating template.pot
Continuing our merge.sh
script, we then check to see if an older template.pot
file exists.
If it does, we’ll replace the POT-Creation-Date
in the new file with the older creation date, then run the diff
command to detect if there’s been any changes. If there has been changes, we fix the POT-Creation-Date
and overwrite the old template.pot
file. To make the changes more noticeable, we also list the added/removed translation messages.
If there hasn’t been any changes, we simply delete the template.pot.new
file.
Lastly, we delete the infiles.list
to clean things up.
Examining template.pot
Now that we’ve got a template.pot
, let’s take a look at it.
The messages we want to translate appear as msgid "Show Thing"
, with the file it came from appearing in a comment in the line above. Underneath is an empty msgstr ""
which is where the translator will place the translated messages.
fr.po
Now that we’ve got a template.pot
, our translators can copy it and rename it to fr.po
.
We use fr
since it is the locale code for French, which we’ll be using later.
A full list of locale codes can be found on StackOverflow. Make sure you use underscores (fr_CA
) instead of dashes (fr-CA
) if the language you are translating is not reusable for the generic fr
language.
Translators can then start filling out the empty msgstr ""
with translations.
Merging updates into fr.po
Our merge.sh
currently only extracts messages into template.pot
. We should next merge any new messages extracted for translation into our fr.po
file.
We’ll first filter the translate directory for .po
files.
Then for each .po
file, we’ll extract the locale code (fr
) from the filename using the basename
command then striping out the file extension.
We then use another GetText command msgmerge
to generate a new fr.po.new
file based on the old fr.po
and the current template.pot
.
Afterwards, we use sed
to replace the LANGUAGE
placeholder with our current locale code in case our translator left them as is.
When we’re done, we overwrite the old fr.po
with fr.po.new
.
translate/merge.sh
Building .mo
Once our fr.po
has been filled out, we can then convert it to a binary .mo
file. So lets get started on our build.sh
script.
The latest copy of my complete
build.sh
script can be found here.
We start with the same code that we used in our merge.sh
script to parse our metadata.desktop
file and get the widget’s namespace. We also reuse the same code to iterate the .po
files.
Then we use another GetText command msgfmt
to convert the fr.po
file into a fr.mo
file.
We then make sure a contents/locale/fr/LC_MESSAGES/
folder exists, creating it if it does not.
Then we copy the fr.mo
to the LC_MESSAGES
folder, renaming it to plasma_applet_com.github.zren.helloworld.mo
. Notice that we put plasma_applet_
in front of the widget’s namespace.
Testing our translations
First make sure you run our build.sh
translation script.
Then we need to override the locale environment variables just for our plasmoidviewer
instance. If you run the locale
command, it should list all the environment variables available to override.
In practice, we only need to override LANG="fr_CA.UTF-8"
and another variable it didn’t list LANGUAGE="fr_CA:fr"
. If your widget is a clock, then you might also need to override LC_TIME="fr_FR.UTF-8"
.
Reusing other translations
While it is bad practice to link to private code, if you know another widget has translated a string, you can use i18nd(domain, string, ...)
to use translations from that domain. Note that a widget’s domain starts with plasma_applet_
, and ends with the widget’s X-KDE-PluginInfo-Name
.
Eg: plasma_applet_com.github.zren.helloworld
An example can be found in org.kde.image
’s main.qml which reuses the same code for the org.kde.slideshow
.
Examples
đź–‰Configurable panel widget width/height
While the user can resize the popup window temporarily with Alt+RightClick+Drag
, it will reset on when the user relogs. To allow the user to permanently configure the popup size in a panel widget, or the size of the compact view in the panel, we’ll need a store the width/height in the config.
So we change to our hardcoded sizes:
into this:
Make sure you still multiply the stored width/height by units.devicePixelRatio
, otherwise your popup will look smaller by default on HiDPI/4k monitors.
To simplify testing, I added Plasmoid.hideOnWindowDeactivate: false
to prevent the popup from closing when you focus the config window.
Next we register the config keys and their default values in the config/main.xml
.
Then create a configuration form in ui/configGeneral.qml
. We use SpinBox
and set the max value to the maximum signed integer value in QML.
Lastly we register the General config tab in config/config.qml
.
Time DataSource
An extremely simple example of this can be found in the “fuzzy clock” widget in the kdeplasma-addons
repo (link).
The new Date()
should be familiar if you come from a javascript background. We could use a Timer with the Date type, but we want to precisely sync all clock widgets so they all show the same time on all screens. This is where Plasma’s DataEngines come in. They are used to share data between widgets. There are various dataengines for notifications, plugged in usb drives (hotplug), and event the weather data so it only has to fetch the data once to show it in all widgets on each screen.
To use the “time” data engine, we use PlasmaCore.DataSource
to connect to it. The “time” needs us to connect to our “Local” timezone. Once connected, it gives us a DateTime object we can access using dataSource.data.Local.DateTime
. This property will update every 60000 milliseconds, or every 1 minute.
We also tell the data engine to align these updates to the next minute. If we want to modify this to update every second, we’d change the interval to interval: 1000
(1 second), then remove the intervalAlignment
assignment since there isn’t an “AlignToSecond”, just a PlasmaCore.Types.NoAlignment
.
A clock can then use Qt’s Qt.formatTime(currentDateTime)
to display the time in a human readable format. You can read more about that function on the Qt documentation for Qt.formatDateTime(...)
.
Avoid widget resize on text change
We use TextMetrics
to calculate the size of the Text label when it is the widest/maximum value of 100%
.
Bundle icon/svg with widget
If we’re packaging a QML only widget to be put on the KDE Store, we won’t be able to install an icon to /usr/share/icons/
. Instead we’ll package the icon in the widget directory. For this example, we will place our icon in contents/icons/customicon.svg
. Copy /usr/share/icons/breeze/apps/22/kde.svg
as a placeholder if you don’t have an icon drawn up yet.
The DefaultCompactRepresentation.qml
uses PlasmaCore.IconItem
, which supports a source: "iconname"
only if the icon is installed to /usr/share/icons/
. Instead we’ll need to use the full path to the svg.
Eg: source: "/path/.../contents/icon/customicon.svg"
Use plasmoid.file('', 'icons/customicon.svg')
to get the absolute path to that file. Note that plasmoid.file()
calls KPackage::filePath()
which will return an empty string if the file does not exist.
Unfortunately, the PlasmaCore.IconItem
image loading logic will not apply the Color Scheme colors if you use an absolute filepath. It only applies the Color Scheme colors if you use source: "iconname"
. To workaround this, we’ll use a PlasmaCore.SvgItem
+ PlasmaCore.Svg
.
I’ve written AppletIcon.qml
which allows you to easily use AppletIcon { source: "customicon" }
to draw the icon with PlasmaCore.SvgItem
. It also supports AppletIcon { source: "kde" }
which first checks to see if icons/kde.svg
exists, then falls back to the kde
icon from the icon theme. This way you can easily support a configrable icon in your widget.
Now that we’ve drawn the icon, we need to fixup customicon.svg
to support the color scheme. I will summarize the Plasma Style (aka desktoptheme) documentation on system colors here.
Open up kde.svg
up in a text editor, you’ll see:
The contents of the <style id="current-color-scheme" />
stylesheet is replaced with generated CSS with the current Color Scheme colors when the icon is loaded. The normal <path style="fill:#111111">
has also been edited to use fill:currentColor
which uses the class="ColorScheme-Text"
color.
Finally, if you edit customicon.svg
, you may need to delete the svg cache to see changes. I usually run it before plasmoidviewer
when testing a widget with a bundled SVG.