Pages
Developer Documentation
On this page

Form delegates in your settings pages

You have learned how to add About pages to your application. Now you can use the same inner components to create your settings pages.

The project structure should look like this:

addonsexample/
├── CMakeLists.txt
├── main.cpp
├── Main.qml
├── MyAboutPage.qml
└── SettingsPage.qml

Needed changes

Add SettingsPage.qml to ecm_target_qml_sources() in CMakeLists.txt, and change Main.qml to include our new settings page:

import QtQuick
import QtQuick.Layouts

import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard

Kirigami.ApplicationWindow {
    id: root
    width: 600
    height: 700

    Component {
        id: aboutsonicde
        FormCard.AboutKDEPage {}
    }

    Component {
        id: aboutpage
        MyAboutPage {}
    }

    Component {
        id: settingspage
        SettingsPage {}
    }

    pageStack.initialPage: Kirigami.ScrollablePage {
        ColumnLayout {
            FormCard.FormCard {
                FormCard.FormButtonDelegate {
                    id: aboutSonicDEButton
                    icon.name: "start-here-sonicde"
                    text: i18n("About SonicDE Page")
                    onClicked: root.pageStack.layers.push(aboutsonicde)
                }

                FormCard.FormButtonDelegate {
                    id: aboutPageButton
                    icon.name: "applications-utilities"
                    text: i18n("About Addons Example")
                    onClicked: root.pageStack.layers.push(aboutpage)
                }

                FormCard.FormButtonDelegate {
                    id: settingsButton
                    icon.name: "settings-configure"
                    text: i18n("Single Settings Page")
                    onClicked: root.pageStack.layers.push(settingspage)
                }
            }
        }
    }
}

Now we can look at the components used to create our settings page: the form card and its delegates.

Form delegates

FormCard and FormCardPage

FormCard.FormCard is the main component we use to group its child components, the delegates.

We used a form card in the Kirigami Addons introduction before. Its main purpose is to be a container for other components, in a color different from the background, much like a Kirigami.Card.

Create a new SettingsPage.qml file:

import QtQuick
import org.kde.kirigamiaddons.formcard as FormCard

FormCard.FormCardPage {
    FormCard.FormCard {
        // This is where all our delegates go!
    }

    FormCard.FormCard {
        // This is where all our delegates go!
    }
}

Since our settings page is a separate QML file, and we need to be ready for scrolling, we use a FormCard.FormCardPage, which inherits Kirigami.ScrollablePage.

The nice thing about the form card page is its internal layout: no additional ColumnLayout is needed, and form cards can be added directly to it.

Note: You can use FormCard delegates directly with a Kirigami.ScrollablePage, but then you need to add your own layouts.

FormHeader

For every FormCard you create, you can create a FormHeader just before it. The header uses a heavier font weight and appears right above the form card.

import org.kde.kirigamiaddons.formcard as FormCard

FormCard.FormCardPage {
    FormCard.FormHeader {
        title: i18n("General")
    }

    FormCard.FormCard {
        // Our delegates go here...
    }

    FormCard.FormHeader {
        title: i18n("Accounts")
    }

    FormCard.FormCard {
        // Our delegates go here...
    }
}

FormTextDelegate and FormSectionText

Let's start simple, with plain text.

FormSectionText simply adds a thin delegate containing a label. FormTextDelegate has text and a grayed-out description.

import QtQuick
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard

FormCard.FormCardPage {
    FormCard.FormHeader {
        title: i18n("General")
    }

    FormCard.FormCard {
        FormCard.FormTextDelegate {
            text: i18n("Current Color Scheme")
            description: "Silver Light"
        }
    }

    FormCard.FormHeader {
        title: i18n("Accounts")
    }

    FormCard.FormCard {
        FormCard.FormSectionText {
            text: i18n("Online Account Settings")
        }
        FormCard.FormTextDelegate {
            leading: Kirigami.Icon {source: "user"}
            text: "John Doe"
            description: i18n("The Maintainer ™️")
        }
    }
}

We add some dummy text for the hypothetical color scheme detection. Actual detection could be built on a C++ model with KColorSchemeManager.

In the Online Accounts section there is an additional property, leading. We can give it an Item that appears before the text. Its opposite, trailing, shows an Item after the text, but we don't use it in our program.

We use a Kirigami.Icon here for simplicity, but this could also be a Kirigami Addons Avatar that gets its information from a model.

FormButtonDelegate

The FormButtonDelegate looks similar to a FormTextDelegate, but it is clickable and shows an arrow pointing to the right. We used it in the Kirigami Addons introduction before.

Like the FormTextDelegate, it has leading and trailing properties to show an item before and after the main content; the arrow stays at the right-most side.

import QtQuick
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard

FormCard.FormCardPage {
    FormCard.FormHeader {
        title: i18n("General")
    }

    FormCard.FormCard {
        FormCard.FormTextDelegate {
            text: i18n("Current Color Scheme")
            description: "Silver Light"
        }
    }

    FormCard.FormHeader {
        title: i18n("Accounts")
    }

    FormCard.FormCard {
        FormCard.FormSectionText {
            text: i18n("Online Account Settings")
        }
        FormCard.FormTextDelegate {
            leading: Kirigami.Icon {source: "user"}
            text: "John Doe"
            description: i18n("The Maintainer ™️")
        }
        FormCard.FormButtonDelegate {
            icon.name: "list-add"
            text: i18n("Add a new account")
            onClicked: console.info("Clicked!")
        }
    }
}

We use its icon.name property to show a plus (+) icon after the space for the leading item and before the main content. This is a common pattern to show that your button adds something to a list.

As this example is purely illustrative, we don't go into what happens when the button is clicked: it just prints "Clicked!" to the terminal. We could make a new page for account creation that adds another user to a model, and push that page into view, as we did in Main.qml.

FormRadioDelegate, FormCheckDelegate and FormSwitchDelegate

RadioButton, CheckBox and Switch are very common in any user interface. Kirigami Addons provides them as FormRadioDelegate, FormCheckDelegate and FormSwitchDelegate.

Their main properties are just text and description. In use they all inherit AbstractButton, so you use its properties and signal handlers: checked and onCheckedChanged, onToggled, and onClicked.

We want some autosave functionality in our application, and to show its settings only if the user has turned it on. Create a new section with a FormCard and a FormHeader, then add a FormSwitchDelegate and some FormRadioDelegate components.

FormCard.FormHeader {
    title: i18n("Autosave")
}

FormCard.FormCard {
    FormCard.FormSwitchDelegate {
        id: autosave
        text: i18n("Enabled")
    }
    FormCard.FormRadioDelegate {
        text: i18n("After every change")
        visible: autosave.checked
    }
    FormCard.FormRadioDelegate {
        text: i18n("Every 10 minutes")
        visible: autosave.checked
    }
    FormCard.FormRadioDelegate {
        text: i18n("Every 30 minutes")
        visible: autosave.checked
    }
}

We bind the visibility of each radio button to the switch, so they only appear when the switch is on.

Best practices: If you have a background in imperative languages such as C++, you might be tempted to change the visibility of the radio buttons from JavaScript when the switch's checked property changes:

onCheckedChanged: {
    radio1.visible = checked;
    radio2.visible = checked;
    radio3.visible = checked;
}

This doesn't suit QML's declarative language. Use QML bindings, like visible: autosave.checked, as much as possible instead of JavaScript assignments. See Qt's best practices for details.

To try our checkbox, add a new FormCheckDelegate to our General section.

FormCard.FormHeader {
    title: i18n("General")
}

FormCard.FormCard {
    FormCard.FormTextDelegate {
        text: i18n("Current Color Scheme")
        description: "Silver Light"
    }
    FormCard.FormCheckDelegate {
        text: i18n("Show Tray Icon")
        onToggled: {
            if (checkState) {
                console.info("A tray icon appears on your system!")
            } else {
                console.info("The tray icon disappears!")
            }
        }
    }
}

Here we use the signal handler onToggled to print some dummy text that simulates a tray icon appearing on the system. A real tray icon could be built with KStatusNotifierItem or Qt's SystemTrayIcon.

FormComboBoxDelegate

The common ComboBox component is available as a FormComboBoxDelegate.

It has several useful properties: editable, displayText and displayMode.

Setting editable: true lets the user edit the text of the combobox, which is useful if new options may need adding.

To show additional text before each option, use something like displayText: "Profile: " + currentText.

The most interesting one, which we use in our example, is displayMode. It has three options:

  • FormComboBoxDelegate.ComboBox: the standard small box showing a list of options. This is the default on the desktop.
  • FormComboBoxDelegate.Dialog: a dialog showing a list of options in the middle of the window. This is the default on mobile.
  • FormComboBoxDelegate.Page: a new page with the list of options, shown in a separate window.

Add the following between the "Current Color Scheme" and "Show Tray Icon" delegates in your "General" form card.

FormCard.FormComboBoxDelegate {
    text: i18n("Default Profile")
    description: i18n("The profile to be loaded by default.")
    displayMode: FormCard.FormComboBoxDelegate.ComboBox
    currentIndex: 0
    editable: false
    model: ["Work", "Personal"]
}

FormDelegateSeparator

Our settings page is taking shape, but each section is getting long. We can add a few FormDelegateSeparator instances to make the page tidier:

import QtQuick
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard

FormCard.FormCardPage {
    id: root

    title: i18nc("@title", "Settings")

    FormCard.FormHeader {
        title: i18nc("@title:group", "General")
    }

    FormCard.FormCard {
        FormCard.FormTextDelegate {
            text: i18nc("@info", "Current Color Scheme")
            description: "Silver Light"
        }

        FormCard.FormComboBoxDelegate {
            id: combobox
            text: i18nc("@label:listbox", "Default Profile")
            description: i18nc("@info:whatsthis", "The profile to be loaded by default.")
            displayMode: FormCard.FormComboBoxDelegate.ComboBox
            currentIndex: 0
            editable: false
            model: ["Work", "Personal"]
        }

        FormCard.FormDelegateSeparator {
            above: combobox
            below: checkbox
        }

        FormCard.FormCheckDelegate {
            id: checkbox
            text: i18nc("@option:check", "Show Tray Icon")
            onToggled: {
                if (checkState) {
                    console.info("A tray icon appears on your system!")
                } else {
                    console.info("The tray icon disappears!")
                }
            }
        }
    }

    FormCard.FormHeader {
        title: i18nc("@title:group", "Autosave")
    }

    FormCard.FormCard {
        FormCard.FormSwitchDelegate {
            id: autosave
            text: i18nc("@option:check", "Enabled")
        }
        FormCard.FormDelegateSeparator {
            above: autosave
            below: firstradio
            visible: autosave.checked
        }
        FormCard.FormRadioDelegate {
            id: firstradio
            text: i18nc("@option:radio", "After every change")
            visible: autosave.checked
        }
        FormCard.FormRadioDelegate {
            text: i18nc("@option:radio", "Every 10 minutes")
            visible: autosave.checked
        }
        FormCard.FormRadioDelegate {
            text: i18nc("@option:radio", "Every 30 minutes")
            visible: autosave.checked
        }
    }

    FormCard.FormHeader {
        title: i18nc("@title:group", "Accounts")
    }

    FormCard.FormCard {
        FormCard.FormSectionText {
            text: i18nc("@info:whatsthis", "Online Account Settings")
        }
        FormCard.FormTextDelegate {
            id: lastaccount
            leading: Kirigami.Icon {source: "user"}
            text: "John Doe"
            description: i18nc("@info:credit", "The Maintainer ™️")
        }
        FormCard.FormDelegateSeparator {
            above: lastaccount
            below: addaccount
        }
        FormCard.FormButtonDelegate {
            id: addaccount
            icon.name: "list-add"
            text: i18nc("@action:button", "Add a new account")
            onClicked: console.info("Clicked!")
        }
    }
}

Generally, use separators wherever you see major distinctions between components, although where to place them is ultimately your choice. For example, in the General section the checkbox differs from the components before it, as it doesn't start with text; in the Autosave section the separator groups the radio buttons together; and in the Accounts section a separator between the last account and the button gives the button some additional focus.

The above and below properties hold the components above and below the separator, and the separator disappears whenever one of them is hovered or highlighted. By default they are its neighbours in the layout, so you only need to set them when you want other components. For example, the separator above the tray icon setting doesn't appear while that setting is hovered.

If you want a separator between every pair of delegates in a form card, set its autoSeparators property to true instead of adding them by hand.

Adapted for SonicDE from develop.kde.org/docs/getting-started/kirigami/addons-delegates/ by Florian Ruechel, Melissa Autumn, Thiago Sueto, licensed under CC BY-SA 4.0.

Edit this page on GitHub

We use cookies to personalize content and to analyze our traffic. Please decide if you are willing to accept cookies from our website.