Компонування, панелі списків і картки

Розбираємося із різними способами розташовування елементів на сторінці

Компонування ваших даних

Тепер, коли ми вже розуміємо, як працюють сторінки, час додати можливості до наших. Ми розглянемо декілька важливих компонентів та елементів компонування, які будуть корисними при створенні дизайну нашої програми.

Не лякайтеся великих фрагментів коду! Ми розберемо усе, що ми ще не обговорювали раніше, і наприкінці цього розділу ви матимете програму, яка виглядатиме лаконічно.

ListView

Якщо ви колись користувалися Discover, NeoChat або Системними параметрами Плазми, ви вже бачили ListView. Доволі прості, ListView надають вам змогу показувати дані у форматі списку.

Kirigami.CardsListView {
    id: cardsView
    model: kountdownModel
    delegate: kountdownDelegate
}

Це може здатися якимось шифром, але не переймайтеся. Розпочнімо з початку.

Ми додаємо цей компонент до нашого Kirigami.ScrollablePage з останньої частини підручника.

Ми скористалися Kirigami.CardsListView . Це ListView, який надає нам змогу просто показувати картки у форматі списку. Втім, ListView створено для показу даних, які взято з моделі — для автоматичного заповнення списку набором даних, які ми вказали. Ось тут нам стане у пригоді властивість model — у нашому прикладі, вона вказує на kountdownModel.

Модель

ListModel {
    id: kountdownModel
    // Кожне з ListElement є елементом списку, що містить дані
    ListElement {
        name: "Dog birthday!!"
        description: "Big doggo birthday blowout."
        date: 100
    }
}

Ми додаємо нашу kountdownModel до Kirigami.ApplicationWindow з останньої частини підручника.

Модель визначає спосіб, у який структуровано запис даних. Зараз наша kountdownModel складається лише з одного елемента. Структурування дані у нашій kountdownModel можна бачити у нашому ListElement: вони містять назву, опис і дату. Це не жорстка структура, і у вашій моделі можуть бути інші типи даних. Перші два елементи є лише рядками, а третій є числом, яким ми користуємося як замінником.

Крім того, моделі є корисними через те, як їх можна змінювати, використовуючи декілька методів. Серед важливих методів такі:

  • ListModel.append(yourobject: jsobject) додає вказаний вами об'єкт JavaScript до ListModel і розташовує його після останнього запису у моделі. Щоб це відбувалося належним чином, вам слід надати об'єкт JavaScript із належними властивостями і відповідними типами даних.
  • ListModel.get(index: int) повертає JSObject із наданим індексом розташування.
  • ListModel.remove(index: int, count: int) вилучає JSObject із наданим індексом розташування і вказану кількість count об'єктів після нього (1 включає лише JSObject із наданим індексом)
  • ListModel.set(index: int, ваш_об'єкт: jsobject) замінює значення запису із наданим індексом розташування значеннями наданого вами об'єкта yourobjectваш_об'єкт. Ті самі правила, що і для append().

Делегат

Хоча наша kountdownModel містить дані, які буде показано, наш kountdownDelegate оброблятиме спосіб показу даних у ListView. Kirigami.CardsListView було розроблено для показу делегатів типу карток, і ми справді використали елемент Kirigami.AbstractCard як наш делегат у фрагменті вище.

Делегати автоматично отримують властивості ListElement, які вказано у нашій моделі. Тому ми можемо просто посилатися на властивості name, description і date наших ListElement так, наче це була б звичайна змінна у нашому делегаті.

Побудова нашої делегованої картки

Компонент , який представляє нашого делегата, можна додати всередині нашого Kirigami.ApplicationWindow . Далі, ми продовжимо знайомитися з тим, яке завдання виконує кожна частина нашого компонента-делегата.

Component {
    id: kountdownDelegate
    Kirigami.AbstractCard {
        contentItem: Item {
            // implicitWidth/Height визначають природну ширину і висоту пункту, якщо ширину або
            // висоту не вказано явним чином. Наведений нижче код визначає бажані розміри компонента
            // на основі його вмісту.
            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 < 100000) ? date : i18n("%1 days", Math.round((date-Date.now())/86400000))
                }

                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: буде зроблено… невдовзі!
                }
            }
        }
    }
}

implicitWidth та implicitHeight

Перша частина, яку ми розглянемо, полягає у тому, як керувати шириною та висотою нашого компонента:

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

Розглядаючи наш Kirigami.AbstractCard , першими встановленими нами властивостями є implicitWidth і implicitHeight. Ми надаємо їм значення delegateLayout.implicitWidth і delegateLayout.implicitHeight, тобто implicitWidth і implicitHeight елемента GridLayout. Неявними ширинами і висотами є властивості, які встановлено подібно до типових, тобто якщо немає явно заданих ширини і висоти для цих компонентів. Тому ми встановили для implicitWidth і implicitHeight нашого Kirigami.AbstractCard у значення GridLayout нижче, щоб GridLayout не розповзся за картку.

Компонування

GridLayout перебуває всередині компонента Item, який ми надали для властивості contentItem. Це Item, який містить те, що буде показано у вашій картці.

Нам також потрібно вибрати компонування для наших компонентів так, щоб вони не просто накопичувалися один над одним. Існує три основних типи, один з яких ми можемо вибрати:

  • ColumnLayout розташовує ваші компоненти вертикально, у один стовпчик
  • RowLayout розкладає ваші компоненти горизонтально у один рядок
  • GridLayout розкладає ваші компоненти у таблицю із вибраним вами компонуванням

З ColumnLayout і RowLayout усе, що нам слід зробити, — це записати наші компоненти всередину компонента Layout. Як ви можете бачити, ми використали GridLayout, що потребує додаткового опису вручну.

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

Першим, що ви бачите, є наш anchor. Система прив'язок QtQuick надає зручний спосіб визначення розташування ваших компонентів у певних місцях батьківського компонента. Ми прив'язали наш GridLayout ліворуч, згори і праворуч до батьківської картки, забезпечивши розтягування вмісту на усю картку.

Далі, ми вказали інтервал між рядками і стовпчиками у нашій таблиці, щоб наші компоненти не накладалися один на одного. У Kirigami передбачено декілька зручних попередньо визначених одиниць для використання з цією метою:

Одиниця KirigamiПікселі
smallSpacing4px
largeSpacing8px
gridUnit18px

Як ви можливо пам'ятаєте, root є ідентифікатором нашого Kirigami.ApplicationWindow . Він надає властивість wideScreen , яку використовують для визначення того, чи є поточний екран пристрою широкоформатним (тобто монітором комп'ютера або екраном телефону в альбомному режимі). Тут ми використовуємо тернарну умову для зміни кількості стовпчиків у нашій таблиці залежно від використаного екрана: якщо він є широкоформатним, у таблиці буде 4 стовпчики, інакше їх буде 2.

Внутрішні компоненти

Ми могли б просто створити три мітки у нашому компоненті-делегаті, і все. Але це не виглядало б аж надто привабливо.

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

Знімок вигляду Card

  • Ліворуч, Kirigami.Heading : використовує date з ListElement як заголовок першого рівня.
  • Посередині, ColumnLayout: містить Kirigami.Heading , де показано назву завдання; Kirigami.Separator , який додає горизонтальну лінію; і Controls.Label , який показу необов'язковий опис завдання. Останні два компоненти мають властивість visible, значення якої визначається перевіркою того, чи є порожнім опис, — компоненти буде показано, залежно від результату перевіркиdescription.length > 0.
  • Праворуч, Controls.Button : кнопка, яка щось робитиме… невдовзі!

Наша програма на поточний момент

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.20 as Kirigami

Kirigami.ApplicationWindow {
    id: root

    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 {
                        // Heading will be as tall as possible while respecting constraints
                        Layout.fillHeight: true
                        // 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
        }
    }
}

Знімок вигляду вікна програми після завершення цього уроку

Отже, маємо нашу базову картку!

Виконанням цих кроків ми заклали підвалини для додавання функціональних можливостей до нашої програми.