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
  • Laying out your content
  • ListViews
  • Our app so far
  1. KDE Developer Platform
  2. Getting started
  3. Kirigami

Layouts, ListViews, and Cards

Figuring out the different ways of placing things on a page

PreviousExplaining pagesNextAdding actions

Last updated 8 months ago

Laying out your content

Now that we understand how pages work, it is time to add stuff to them. We will be going through a number of important layout components and elements that will be useful when designing our app.

By the end of this section you'll have a neat-looking app.

ListViews

If you've ever used , , or Plasma's System Settings, you will have come across a . Quite simply, it lets you display data on a list.

pageStack.initialPage: Kirigami.ScrollablePage {
    // ...
    Kirigami.CardsListView {
       id: cardsView
        model: kountdownModel
        delegate: kountdownDelegate
    }
}

That seems cryptic, but don't worry. Let's start from the beginning.

We add this component inside our from the last tutorial.

We're using , which is a that allows us to easily display cards in a list. However, ListViews are made to show data taken from a model - to automatically populate itself from a set of data that we point it to. That's where the model property comes in: in this example, it's pointing to kountdownModel.

Model

Kirigami.ApplicationWindow {
    // ...
    ListModel {
        id: kountdownModel
        // Each ListElement is an element on the list, containing information
        ListElement {
            name: "Dog birthday!!"
            description: "Big doggo birthday blowout."
            date: 100
        }
    }
    // ...
}

Note

Models are also useful in how they can be modified through the use of several methods. Some important ones are:

Delegates

Building the delegate card

Kirigami.ApplicationWindow {
    // ...
    Component {
        id: kountdownDelegate
        Kirigami.AbstractCard {
            contentItem: Item {
                // implicitWidth/Height define the natural width/height
                // of an item if no width or height is specified.
                // The setting below defines a component's preferred size based on its content
                implicitWidth: delegateLayout.implicitWidth
                implicitHeight: delegateLayout.implicitHeight
                GridLayout {
                    id: delegateLayout
                    anchors {
                        left: parent.left
                        top: parent.top
                        right: parent.right
                    }
                    rowSpacing: Kirigami.Units.largeSpacing
                    columnSpacing: Kirigami.Units.largeSpacing
                    columns: root.wideScreen ? 4 : 2

                    Kirigami.Heading {
                        Layout.fillHeight: true
                        level: 1
                        text: date
                    }

                    ColumnLayout {
                        Kirigami.Heading {
                            Layout.fillWidth: true
                            level: 2
                            text: name
                        }
                        Kirigami.Separator {
                            Layout.fillWidth: true
                            visible: description.length > 0
                        }
                        Controls.Label {
                            Layout.fillWidth: true
                            wrapMode: Text.WordWrap
                            text: description
                            visible: description.length > 0
                        }
                    }
                    Controls.Button {
                        Layout.alignment: Qt.AlignRight
                        Layout.columnSpan: 2
                        text: i18n("Edit")
                        // onClicked: to be done... soon!
                    }
                }
            }
        }
    }
    // ...
}

implicitWidth and implicitHeight

The first part we will take a look at is how to manage the width and height of our component:

Kirigami.AbstractCard {
    contentItem: Item {
        implicitWidth: delegateLayout.implicitWidth
        implicitHeight: delegateLayout.implicitHeight
        GridLayout {
            id: delegateLayout
            // ...
        }
    }
}

Layouts

GridLayout {
    id: delegateLayout
    anchors {
        left: parent.left
        top: parent.top
        right: parent.right
    }
    rowSpacing: Kirigami.Units.largeSpacing
    columnSpacing: Kirigami.Units.largeSpacing
    columns: root.wideScreen ? 4 : 2
    // ...
}
Kirigami Unit
Pixels

smallSpacing

4px

largeSpacing

8px

gridUnit

18px

Note

Inner components

We could just create three labels within our delegate component and call it a day, but that wouldn't look particularly nice. We'll make use of a few more convenient components:

GridLayout {
    // ...
    Kirigami.Heading {
        Layout.fillHeight: true
        level: 1
        text: date
    }

    ColumnLayout {
        Kirigami.Heading {
            Layout.fillWidth: true
            level: 2
            text: name
        }

        Kirigami.Separator {
            Layout.fillWidth: true
            visible: description.length > 0
        }

        Controls.Label {
            Layout.fillWidth: true
            wrapMode: Text.WordWrap
            text: description
            visible: description.length > 0
        }
    }

    Controls.Button {
        Layout.alignment: Qt.AlignRight
        Layout.columnSpan: 2
        text: i18n("Edit")
    }
}

Our app so far

Main.qml:
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    id: root

    width: 400
    height: 300

    title: i18nc("@title:window", "Day Kountdown")

    // ListModel needed for ListView, contains elements to be displayed
    ListModel {
        id: kountdownModel
        // Each ListElement is an element on the list, containing information
        ListElement {
            name: "Dog birthday!!"
            description: "Big doggo birthday blowout."
            date: 100
        }
    }

    Component {
        id: kountdownDelegate
        Kirigami.AbstractCard {
            contentItem: Item {
                implicitWidth: delegateLayout.implicitWidth
                implicitHeight: delegateLayout.implicitHeight
                GridLayout {
                    id: delegateLayout
                    anchors {
                        left: parent.left
                        top: parent.top
                        right: parent.right
                    }
                    rowSpacing: Kirigami.Units.largeSpacing
                    columnSpacing: Kirigami.Units.largeSpacing
                    columns: root.wideScreen ? 4 : 2

                    Kirigami.Heading {
                        // Level determines the size of the heading
                        level: 1
                        text: date
                    }

                    // Layout for positioning elements vertically
                    ColumnLayout {
                        Kirigami.Heading {
                            Layout.fillWidth: true
                            level: 2
                            text: name
                        }
                        // Horizontal rule
                        Kirigami.Separator {
                            Layout.fillWidth: true
                            visible: description.length > 0
                        }
                        // Labels contain text
                        Controls.Label {
                            Layout.fillWidth: true
                            // Word wrap makes text stay within box and shift with size
                            wrapMode: Text.WordWrap
                            text: description
                            visible: description.length > 0
                        }
                    }
                    Controls.Button {
                        Layout.alignment: Qt.AlignRight
                        // Column spanning within grid layout (vertically in this case)
                        Layout.columnSpan: 2
                        text: i18n("Edit")
                        //onClicked: to be done...
                    }
                }
            }
        }
    }

    pageStack.initialPage: Kirigami.ScrollablePage {
        title: i18nc("@title", "Kountdown")

        // List view for card elements
        Kirigami.CardsListView {
            id: cardsView
            // Model contains info to be displayed
            model: kountdownModel
            // Delegate is how the information will be presented in the ListView
            delegate: kountdownDelegate
        }
    }
}

So there is our basic card!

With these steps, we have now laid the basic groundwork for adding all the functionality to our app.

We add our kountdownModel inside the from the last tutorial.

A model defines the way that a data entry is structured. Our kountdownModel will consist of only one element for now. By looking at our above, we can see how the data of our kountdownModel are structured: it contains a name, a description, and a date. This isn't set in stone, and you may have different sorts of data in your model. The first two are just strings, and the third is a number we're using as a placeholder.

Since QML is built on top of JavaScript, many of this language's features are available for use in QML files. However, QML variables have to be prefixed with property, unless it is inside a JS code block. You can read more about it in .

adds a JavaScript object (JSObject) yourobject to the ListModel and places it after the last item in the model. For this to happen correctly, you must provide a JSObject with the correct properties and corresponding datatypes.

returns the JSObject at the index location you provide.

removes the JSObject at the provided index location, and as many after that index location as you put in count (1 includes only the JSObject at the provided index)

changes the item at the provided index location with the values provided in yourobject. Same rules as with append().

While our kountdownModel contains the data that will be displayed, our kountdownDelegate will handle how the data will be displayed in the . For that we use a designed to display card-type delegates, and those delegates will be visually represented by means of a .

Delegates automatically receive the properties of the that we have specified in our model. We can therefore just refer to their name, description, and date properties as if they were conventional variables within our delegate.

The that will represent our delegate can be added inside our . We will then check what each part of our delegate component does.

Looking at our , the first properties we set are and . We have set these to the delegateLayout.implicitWidth and delegateLayout.implicitHeight, i.e. the implicitWidth and implicitHeight of the GridLayout element.

Implicit widths and heights are properties available in any that function as hints and are set as a default, or as a fallback, if there is no explicit width or height set for these components. These values default to 0x0, so it is very important that you define those in raw Item components as done above.

Here we have set the implicitWidth and implicitHeight of our to that of the below to ensure it does not spill out of the card. This way, the card takes as much space is necessary for its contents.

The is inside the component we have provided for the property . This is the that contains what will be displayed in your card.

We also need to choose a for our components so that they don't just pile on top of each other. There are three main types that we can choose from:

lays out your components vertically, in a single column

lays out your components horizontally, in a single row

lays out your components in a grid with a composition of your choosing

With and , all we have to do is write our components inside the Layout component. As you can see, we went with a , which entails a bit more handiwork.

The first thing you see is our anchors. provides a useful way of making sure your components are positioned in certain parts of a parent component. We have anchored our to the left, top, and right of the parent card, ensuring our content stretches across the whole card.

Next we specify the spacing between the rows and columns within our grid, so that our components don't bunch up. Kirigami provides a number of handy to use for this purpose:

KDE's Visual Design Group (VDG) has a lot more information about the different units defined within Plasma and Kirigami on the .

As you might remember, root is the id of our . It provides the property, used to determine whether the current device screen is a widescreen (i.e. a computer monitor or a phone in landscape). We use a here to vary the number of columns in our grid depending on the screen we are using: if it's a widescreen, the grid will have 4 columns, else it will have 2.

Left, : uses the ListElement's date as a level 1 heading.

Middle, : has a that displays the task name; a , which provides the horizontal line; and a , that displays a task's optional description. The latter two components have a property, which checks if the description is empty or not and displays the components depending on the result of description.length > 0.

Right, : a button that will do something... soon!

Discover
NeoChat
ListView
Kirigami.ScrollablePage
Kirigami.CardsListView
ListView
Kirigami.ApplicationWindow
ListElement
this page
ListModel.append(yourobject: jsobject)
ListModel.get(index: int)
ListModel.remove(index: int, count: int)
ListModel.set(index: int, yourobject: jsobject)
ListView
Kirigami.CardsListView
Kirigami.AbstractCard
ListElements
Component
Kirigami.ApplicationWindow
Kirigami.AbstractCard
implicitWidth
implicitHeight
Item
Kirigami.AbstractCard
GridLayout
GridLayout
Item
contentItem
Item
layout
ColumnLayout
RowLayout
GridLayout
ColumnLayout
RowLayout
GridLayout
QtQuick's anchoring system
GridLayout
predefined units
Human Interface Guidelines
Kirigami.ApplicationWindow
wideScreen
ternary conditional
Kirigami.Heading
ColumnLayout
Kirigami.Heading
Kirigami.Separator
Controls.Label
visible
Controls.Button
How the custom Card looks like
Screenshot of the app appearance after completing this lesson