Porting KCM modules from QtWidgets to QtQuick/Kirigami for GSOC 2022

I’ve been selected for GSOC this year. My task is to redesign and port the KCMs currently in Qt Widgets to QtQuick/Kirigami

Thanks, Nate and David for agreeing to mentor me.

Why do this ?

Before this, I was already working on KCM for setting gamma in KWin. The MR is in work progress because I decided to add these settings option into kscreen’s KCM instead of creating new one which is pending atm. So I had some prior experience with KCMs

When I first saw this task in ideas list as a user, I was like, why bother ? Qt Widgets already perform much better than Qt Quick. Apps and KCMs in Qt Widgets start instantaneously, compared to many Qt Quick/Kirigami apps and KCMs.

In Qt 6, there is work going on Strict QML and Tiny QML. This will significancy improve QML performance and development experience. There is already qmltc In Qt 6.3 in Tech Preview stage. It creates the C++ API for qml elements, so you don’t need to write JavaScript. There is also qmlsc which works behind the scenes (dev does not need to change code) which compiles JS to CPP.

So while QML suck (IMO) at the movement, the future of QML in Qt 6 looks promising. Also, as a dev, It’s really hard to understand the UI code of apps written in Qt Widgets.

This task will help Plasma migrate to Qt6, and then we can use all these improvements to improve performance and startup time of our qml code.

Progress report #1

In the last two weeks, I made a mock UI in kirigami/kdeclarative which does nothing

Appearance Page Options Page

While making this, I encountered a weird bug in Qml which stalled my progress for days. If you use the new inline components in file named with QML Object Types, It gives you segmentation fault without any warning or error message. So it was really hard to find what’s going on. Clearing cache helped, but that’s not a good solution, so I decided to find why it happens. I got help from another fellow contributor on matrix. He made a YouTube Video of how we discovered the root cause of this bug. Bug is reported on bugreports.qt.io

After that, I encountered another weird behavior in KDeclarative which further stalled my progress

When pushing new page as filename, the context properties do not worked as I expected.

In following code,

id: root
property var gelement : SomeTem {}
...

function onClicked() {
    kcm.push("Another.qml")
}

I was expecting to access the gelement property in Another.qml file with gelement or root.glement as it worked before when using ApplicationWindow and pageStack.push

This is wrong and also an antipattern and strict qml will properly forbid doing this.

I was very curious why this happens, so decided to investigate. After looking in KDeclarative code and not find anything, So I decide to create the vanilla Qt Quick Application with StackView.

It turns out that PageRow does some magic behind the scenes and kcm.push is not doing anything wrong.

//main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15

Window {
    id:  root
    width: 640
    height: 480
    visible: true

    property string gt:  "This is global text from page 1"
    property alias stack: stackview

    StackView {
        id: stackview
        anchors.fill: parent
        Component.onCompleted: push("qrc:/Page1.qml")
    }
}
//Page1.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
Page {
    id: page1

    property string textFromPage1: "text from page1"
    property var page1p: page1

    Column {
        Label { text: "Page 1" }
        Button {
            text: "Page 2"
            onClicked: {
                stack.push("qrc:/Page2.qml")
            }
        }
    }
}
//Page2.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
Page {
    Label {
        // text:  gt // this work
        text:  page1p.textFromPage1 // this will not
    }
}

If you run above code in gammaray, you will find that both Page1.qml and Page2.qml share the context of main.qml page which is why they both can access stack But they both have their own context which they don’t share with each other.

My guess is that stack.push function is called in context on main.qml since stack is in main.qml

While the last two weeks were not very productive, I learned many new things about Qml and KDeclarative and how it works internally.