Examples

Resizable popup, clock, bundle icon and other simple 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:

// ui/main.qml
Item {
    id: widget
    Plasmoid.fullRepresentation: Item {
        Layout.preferredWidth: 640 * units.devicePixelRatio
        Layout.preferredHeight: 480 * units.devicePixelRatio
    }
}

into this:

// ui/main.qml
Item {
    id: widget
    Plasmoid.fullRepresentation: Item {
        Layout.preferredWidth: plasmoid.configuration.width * units.devicePixelRatio
        Layout.preferredHeight: plasmoid.configuration.height * units.devicePixelRatio
    }
}

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.

// ui/main.qml
Item {
    id: widget
    Plasmoid.fullRepresentation: Item {
        id: popupView
        Layout.preferredWidth: plasmoid.configuration.width * units.devicePixelRatio
        Layout.preferredHeight: plasmoid.configuration.height * units.devicePixelRatio
        Plasmoid.hideOnWindowDeactivate: false
        ColumnLayout {
            id: layout
            anchors.fill: parent
            PlasmaComponents.Label {
                text: i18n("Size: %1 x %2", popupView.width, popupView.height)
            }
        }
    }
}

<!-- config/main.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
    <kcfgfile name=""/>

    <group name="General">
        <entry name="width" type="int">
            <default>640</default>
        </entry>
        <entry name="height" type="int">
            <default>480</default>
        </entry>
    </group>
</kcfg>

// ui/configGeneral.qml
import QtQuick 2.0
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.4 as Kirigami

Item {
    id: page
    width: childrenRect.width
    height: childrenRect.height

    property alias cfg_width: widthSpinBox.value
    property alias cfg_height: heightSpinBox.value

    Kirigami.FormLayout {
        anchors.left: parent.left
        anchors.right: parent.right

        RowLayout {
            Kirigami.FormData.label: i18n("Size:")
            SpinBox {
                id: widthSpinBox
                from: 0
                to: 2147483647 // 2^31-1
            }
            Label {
                text: " x "
            }
            SpinBox {
                id: heightSpinBox
                from: 0
                to: 2147483647 // 2^31-1
            }
        }
    }
}

// config/config.qml
import QtQuick 2.0
import org.kde.plasma.configuration 2.0

ConfigModel {
    ConfigCategory {
        name: i18n("General")
        icon: "configure"
        source: "configGeneral.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(...).

import QtQuick 2.0
import QtQuick.Layouts 1.1

import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.calendar 2.0 as PlasmaCalendar

Item {
    id: root

    readonly property date currentDateTime: dataSource.data.Local ? dataSource.data.Local.DateTime : new Date()

    width: units.gridUnit * 10
    height: units.gridUnit * 4

    Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation

    Plasmoid.toolTipMainText: Qt.formatTime(currentDateTime)
    Plasmoid.toolTipSubText: Qt.formatDate(currentDateTime, Qt.locale().dateFormat(Locale.LongFormat))

    PlasmaCore.DataSource {
        id: dataSource
        engine: "time"
        connectedSources: ["Local"]
        interval: 60000
        intervalAlignment: PlasmaCore.Types.AlignToMinute
    }

    Plasmoid.compactRepresentation: FuzzyClock { }

    Plasmoid.fullRepresentation: PlasmaCalendar.MonthView {
        Layout.minimumWidth: units.gridUnit * 20
        Layout.minimumHeight: units.gridUnit * 20

        today: currentDateTime
    }
}

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%.
import QtQuick 2.4
import QtQuick.Layouts 1.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.plasmoid 2.0

Item {
    id: widget
    property int value: 0
    property int maxValue: 100
    function formatText(n) {
        return "" + n + "%"
    }

    Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation

    Plasmoid.compactRepresentation: PlasmaComponents.Label {
        id: label
        Layout.minimumWidth: textMetrics.width
        Layout.minimumHeight: textMetrics.height

        text: widget.formatText(value)

        font.pointSize: 40
        horizontalAlignment: Text.AlignHCenter

        TextMetrics {
            id: textMetrics
            font.family: label.font.family
            font.pointSize: label.font.pointSize
            text: widget.formatText(100)
        }

        // Since we overrode the default compactRepresentation,
        // we need to setup the click to toggle the popup.
        MouseArea {
            anchors.fill: parent
            onClicked: plasmoid.expanded = !plasmoid.expanded
        }
    }

    Plasmoid.fullRepresentation: Item {
        Layout.preferredWidth: 640 * units.devicePixelRatio
        Layout.preferredHeight: 480 * units.devicePixelRatio

        Rectangle {
            id: popup
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            width: parent.width * (widget.value / 100)
            color: theme.highlightColor
        }
    }

    Timer {
        interval: 100
        running: true
        repeat: true
        onTriggered: widget.value = (widget.value + 1) % (widget.maxValue+1)
    }
}