Nova Flow OS
KDE Developer Platform
KDE Developer Platform
  • KDE Developer Platform
    • Getting started
      • Building KDE software
        • KDE software
        • Where to find the development team
        • Learning more
        • Choose what to work on
        • Source code cross-referencing
        • Installing build dependencies
        • Set up a development environment
        • Building KDE software with kdesrc-build
        • Basic troubleshooting
        • Tips and tricks
        • IDE Configuration
          • Setting up an IDE for KDE development
          • Visual Studio Code
          • Qt Creator
          • Kate
          • KDevelop
          • CLion
          • Sublime Text
        • Building KDE software manually
        • Building KDE software with distrobox and podman
      • Kirigami
        • KDE is ours
        • Setting up and getting started
        • Explaining pages
        • Layouts, ListViews, and Cards
        • Adding actions
        • Adding a dialog
        • Using separate files
        • Next steps
        • Colors and themes in Kirigami
        • Typography
        • Actions based components
        • Page rows and page stacks
        • Scrollable pages and list views
        • Cards
        • Drawers
        • Chips
        • Dialog types
        • Controls and interactive elements
        • Form layouts
        • Inline messages
        • Action toolbars
        • Progress bars and indicators
        • List views
        • Understanding CMakeLists
        • Figuring out main.cpp
        • Connect logic to your QML user interface
        • Connect models to your QML user interface
        • About page
        • Introduction to Kirigami Addons
        • FormCard About pages
        • Form delegates in your settings pages
      • KXmlGui
        • Getting started with KXmlGui
        • Hello World!
        • Creating the main window
        • Using actions
        • Saving and loading
        • Command line interface
      • Python with Kirigami
        • Apps with QML and Python
        • Your first Python + Kirigami application
        • Creating a Python package
        • Creating a Flatpak
      • Common programming mistakes
      • Adding a new KDE project
    • Features
      • Icons
      • Configuration
        • The KConfig Framework
        • Introduction to KConfig
        • Using KConfig XT
        • KDE Frameworks 6 porting guide
        • Settings module (KCM) development
        • KConfigDialog
      • D-Bus
        • What is D-Bus practically useful for?
        • Introduction to D-Bus
        • Accessing D-Bus interfaces
        • Intermediate D-Bus
        • Creating D-Bus interfaces
        • Using custom types with D-Bus
        • D-Bus autostart services
      • Create your own mouse cursor theme
      • Session management
      • Archives
      • Desktop file
      • KAuth
        • Privilege Escalation
        • Using actions in your applications
      • KIdleTime
      • Akonadi: personal information management
        • Debugging Akonadi Resources
        • Using Akonadi in applications
      • Concurrent programming
      • Solid
      • Sonnet
    • Plasma themes and plugins
      • Getting started
      • Plasma Widget tutorial
        • How to create a plasmoid
        • Setup
        • Porting Plasmoids to KF6
        • Testing
        • QML
        • Plasma's QML API
        • Widget Properties
        • Configuration
        • Translations / i18n
        • Examples
        • C++ API
      • KWin Effects
      • Plasma Desktop scripting
        • Javascript Interaction With Plasma Shells
        • Templates
        • Examples
        • API documentation
        • Configuration keys
      • Plasma Style tutorial
        • Creating a Plasma Style quickstart
        • Understanding Plasma Styles
        • SVG elements and Inkscape
        • Background SVG format
        • System and accent colors
        • Theme elements reference
        • Porting themes to Plasma 5
        • Porting themes to Plasma 6
      • Aurorae window decorations
      • KWin scripting tutorial
        • Quick start
        • KWin scripting API
      • Wallpapers
      • Plasma comic
        • Tutorial
        • Testing and debugging
        • Examples
      • Create a custom Window Switcher
      • KRunner C++ Plugin
        • Basic Anatomy of a Runner
        • KRunner metadata format
    • Applications
      • Creating sensor faces
      • Dolphin
        • Creating Dolphin service menus
      • Kate
        • Kate plugin tutorial
      • KMines
        • Making a KMines theme
      • Writing tests
        • Appium automation testing
    • Packaging
      • Android
        • KDE on Android
        • Building applications for Android
        • Packaging and publishing applications for Android
        • Publishing on Google Play
          • Introduction
          • Packaging your app
          • Adding your app to Google Play
          • Publishing your app
          • Releasing new versions of old apps
        • Porting applications to Android
          • Basic porting
          • Making applications run well on Android
          • Metadata
      • Windows
        • Packaging and publishing applications for Windows
        • Publish your app in the Microsoft Store
          • Packaging your app for the Microsoft Store
          • Submitting your app to the Microsoft Store
      • Plasma Mobile
        • KDE on mobile devices
        • Porting a new device to Plasma Mobile
        • KDE Telephony stack
          • General Overview
          • Kernel layer
          • System daemons
            • General overview
            • Developing Telephony functionality
            • ModemManager Telephony functions
          • Session daemons
          • QML declarative plugin layer
          • KDE application layer
        • Execute applications
      • Distributing KDE software as Flatpak
        • Your first Flatpak
        • Extending your package
        • Nightly Flatpaks and Flathub
        • Testing your Flatpak
    • System administration
      • Shell scripting with KDE dialogs
      • Kiosk: Simple configuration management for large deployment
        • Abstract
        • Introduction to Kiosk
        • Kiosk keys
    • Contribute to the documentation
    • About
      • Readme
      • License
        • Creative Commons Attribution-ShareAlike 4.0 International
        • GNU General Public License 3.0 or later
Powered by GitBook
On this page
  • Needed Changes
  • Form Delegates
  1. KDE Developer Platform
  2. Getting started
  3. Kirigami

Form delegates in your settings pages

Create elegant custom settings pages.

Kirigami Addons is an additional set of visual components that work well on mobile and desktop and are guaranteed to be cross-platform. It uses Kirigami under the hood to create its components.

You have learned how to add About and AboutKDE pages to your application. Now you will be able to use their same inner components to create your settings pages.

The project structure should look like this:

addonsexample
├── CMakeLists.txt
├── main.cpp
├── resources.qrc
└── contents/
    └── ui/
        ├── main.qml
        ├── MyAboutPage.qml
        └── SettingsPage.qml

Needed Changes

Add a new line to our resources.qrc:

Ah ah ah

This file is not available.

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

import org.kde.about 1.0

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

    Component {
        id: aboutkde
        FormCard.AboutKDE {}
    }

    Component {
        id: aboutpage
        MyAboutPage {}
    }

    Component {
        id: settingspage
        SettingsPage {}
    }

    pageStack.initialPage: Kirigami.ScrollablePage {
        ColumnLayout {
            FormCard.FormCard {
                FormCard.FormButtonDelegate {
                    id: aboutKDEButton
                    icon.name: "kde"
                    text: i18n("About KDE Page")
                    onClicked: root.pageStack.layers.push(aboutkde)
                }

                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)
                }
            }
        }
    }
}

We can now start checking out the components used to create our Settings page: the Form Card and its Delegates.

Form Delegates

FormCard and FormCardPage

Create a new contents/ui/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!
    }
}

The nice thing about the form card page is that it comes with an internal layout, so no additional ColumnLayout is needed and our delegates can be added directly to it.

Note It is possible to use FormCard delegates directly with a Kirigami.ScrollablePage, but in that case you will need to add your own layouts.

FormHeader

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: "Breeze"
        }
    }

    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 ™️")
        }
    }
}

It should end up looking like this:

FormButtonDelegate

While the FormTextDelegate had the leading and trailing properties to show an item before and after the main content, the FormButtonDelegate only has the leading property, because the right side is occupied by the arrow.

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: "Breeze"
        }
    }

    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 set a plus (+) icon to appear after the space where the leading would appear, and before the main content. This is a common pattern to indicate your button will add something to a list.

Since this example is for simple illustrative purposes, we don't delve deep into what would be done once 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, then push the page into view, similarly to what we did in main.qml.

FormRadioDelegate, FormCheckDelegate and FormSwitchDelegate

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 a switch, so they only appear when the switch is enabled.

Best Practices

Click here to read more

If you have some programming background in imperative languages such as C++, you might be tempted to set the checked property of the switch to turn the visibility of the radio buttons to true with a JavaScript assignment such as:

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

This is not very efficient for QML's declarative language and its signals and slots. Try to use QML bindings like in the case of visible: autosave.checked as much as possible instead of JavaScript expressions.

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

FormCard.FormCard {
    FormCard.FormTextDelegate {
        text: i18n("Current Color Scheme")
        description: "Breeze"
    }
    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!")
            }
        }
    }
}

So far our application should look like this:

FormComboBoxDelegate

This combobox has several useful properties we can make use of: editable, displayText and displayMode.

Setting editable: true allows the user to edit the text of the combobox, which is useful in case adding new combobox options is needed:

Whenever you need to show additional text before each option, you can use something like displayText: "Profile: " + currentText:

And the most interesting one, which we will be using in our example, is displayMode. It can have three options:

  • FormComboBoxDelegate.ComboBox: the standard small box showing a list of options.

  • FormComboBoxDelegate.Page: a new page containing a 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"]
}

With the checkbox, our Settings page should look like this:

FormDelegateSeparator

Our Settings page is taking shape, but each section is starting to get long. We can add a few FormDelegateSeparators to make our page tidier:

Ah ah ah

This file is not available.

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

The above and below properties are rather self-explanatory when it comes to their use: you pass the id of the components above and below the separator. When they are set, the separator will swiftly disappear whenever the above or below item is highlighted/hovered. They are most useful, for instance, when you need to generate components dynamically and you can't automatically assume which item will come immediately before or after the separator. That would be the case in the Accounts section of our application once the logic to add new accounts were actually implemented, in which case we could always grab the last item in the model to do so.

Notice how the separator above the tray icon setting does not appear while it is hovered.

PreviousFormCard About pagesNextKXmlGui

Last updated 8 months ago

is the main component we will be using to group all its child components, the Delegates.

We used a form card in the before. Its main purpose is to serve as a container for other components while following a color different from the background, in a similar manner to a .

Since we are making a separate QML file for our Settings page, and since we need to prepare for potential scrolling in our page, we use a FormCard.FormCardPage, which inherits .

For every you want to create, you can create a just before it. The header uses bold text and shows up right above the form card.

We add some dummy text for the hypothetical theme detection. If we wanted to have actual color scheme detection, in the future it could be done similarly to (), making use of a C++ model with .

In the Online Accounts section, we see an additional property, leading. We can add an to it so that it appears before the text. Its opposite property, trailing, would be able to show an Item after the text, but we won't use it in our program.

We use a here for simplicity, but this could also be implemented using a that grabs the information from a model, as is done in Neochat .

The is visually similar to a FormTextDelegate, but it is clickable and shows an arrow pointing to the right. We used it in the before.

The RadioButton, CheckBox and Switch are very commonly used components in any user interface. Kirigami Addons provides them as , and .

Their only main properties are text and description. They are different in usage because they all inherit , and so you are expected to use its signals and handlers: checked and onChecked, toggled and onToggled, clicked and onClicked.

We want to create some autosave functionality in our application, and we want to only show its settings if the user has enabled this functionality. Create a new section using a and a , then add a and a .

See for details.

To test our checkbox, we can add a new to our General section.

Here we use the onToggled to show some dummy text to simulate a tray icon appearing in the system. If you really wanted to, you could easily implement a tray icon using .

The common ComboBox component can be created using a .

FormComboBoxDelegate.Dialog: a dialog showing a list of options in the middle of the window, like a .

FormCard.FormCard
Kirigami Addons introduction
Kirigami.Card
Kirigami.ScrollablePage
FormCard
FormHeader
Neochat
code here
KColorSchemeManager
Item
Kirigami.Icon
Kirigami.Avatar
here
FormButtonDelegate
Kirigami Addons introduction
FormRadioDelegate
FormCheckDelegate
FormSwitchDelegate
AbstractButton
FormCard
FormHeader
FormSwitchDelegate
FormRadioDelegate
this page
FormCheckDelegate
signal handler
SystemTrayIcon
FormComboBoxDelegate
Kirigami.OverlaySheet