Quick Window Switchers in KWin 5

One of the oldest usages of QtQuick in KWin are the Window Switchers. During the design it was aimed for having them Plasma-styled just like the previous implementation. This was not as easy as one could hope, because back then we couldn’t use the Dialog component provided by PlasmaCore. So all the various Switcher styles implemented the Plasma styling themselves and the C++ side did some additional adjustments like guessing the blur region. As the Plasma theme recognized that the blur effect is active the FrameSvg used as a background was rendered in the blur-available translucency state. To have it readable it needs to be blurred.

For this we would the correct blur mask of the FrameSvg but that’s not available from the Qml bindings. The hack we went for was to just assume in the C++ side that the switcher is Plasma styled, created another FrameSvg of same size and extracted the blur mask from that. Sounds fragile, doesn’t it?

And of course there came the day when that broke. About a year ago we noticed that the shadow doesn’t work any more and we needed to bring in some more fixes to make that work. If we would have used a PlasmaCore Dialog this would not have been an issue at all.

Now after the Qt 5 port the blur and shadow was once again broken and once again I was about to start fixing it. But nowadays we use the Plasma Dialog in e.g. declarative KWin scripts (the desktop change OSD being a prominent example). So I took a step back and thought about instead of fixing the fragile code to re-think it and make it possible to use the Plasma Dialog or in general QtQuick Windows from inside the Qml code.

Given that this required to adjust all switchers anyway I decided to go a step further and also improve the API a little bit. So far all the important data was exposed as context properties. I’m not a huge fan of context properties as it’s kind of magic. Instead there is now a dedicated switcher item available, which is just a QObject exposing a few properties:

import org.kde.kwin 2.0 as KWin

KWin.Switcher {
    property QAbstractItemModel *model // The model with either the windows or the virtual desktops
    property QRect screenGeometry // The geometry of the active screen (as KWin thinks of active)
    property bool visible // Whether the switcher is currently visible or not, a Window should follow this
    property bool allDesktops // Whether the switcher includes windows from all desktops or only the current one
    property int currentIndex // The index of the selected window - this is controlled from KWin side
}

The model provides the following roles for Window Switchers:

  • caption – The caption of the window
  • desktopName – The name of the virtual desktop the window is on
  • minimized – Boolean whether the window is minimized or not
  • windowId – Window system specific identifier
  • closeable – Boolean whether the window can be closed
  • icon – QIcon containing the window’s icon

With that we can design a very simple thumbnails switcher:

import QtQuick 2.1
import org.kde.kwin 2.0 as KWin
import QtQuick.Window 2.1
 
KWin.Switcher {
    id: switcher
    currentIndex: listView.currentIndex
 
    Window {
        id: window
        flags: Qt.X11BypassWindowManagerHint // important, without it won't work
        visible: switcher.visible
        color: "white"
        x: switcher.screenGeometry.x + switcher.screenGeometry.width / 2 - window.width / 2
        y: switcher.screenGeometry.y + switcher.screenGeometry.height / 2 - window.height / 2
        width: switcher.screenGeometry.width - 100 // keep 50 px space to each side
        height: 200 * (switcher.screenGeometry.width/switcher.screenGeometry.height) + 20
 
        ListView {
            id: listView
            orientation: ListView.Horizontal
            model: switcher.model
            spacing: 10
            anchors.fill: parent
            anchors.leftMargin: 10
            anchors.rightMargin: 10
            delegate: KWin.ThumbnailItem {
                width: 200
                height: width * (switcher.screenGeometry.width/switcher.screenGeometry.height)
                wId: windowId
                brightness: ListView.isCurrentItem ? 1.0 : 0.5
                saturation: brightness
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        listView.currentIndex = index;
                    }
                }
            }
            Connections {
                target: switcher
                onCurrentIndexChanged: {
                    // currentIndex is updated from KWin side - need to keep in sync
                    listView.currentIndex = switcher.currentIndex;
                }
            }
        }
    }
}

I wrote this code directly in the blog post without any testing at all (hey it found one bug in KWin and two in the systemsetting previewer. I should do such stuff more often ;-) ). So let’s see how it looks like:

Aus TabBox

Not a bad result for 50 lines of code. As one can also see from the code it does not do any Plasma styling at all any more. Which is a nice side-effect for the usage of KWin outside the Plasma workspaces. The changes to get Plasma styling is not difficult either. Just use PlasmaCore.Dialog instead of the Window and that’s it.

One thought on “Quick Window Switchers in KWin 5

Comments are closed.