Uzante apartajn dosierojn kaj signalojn

Disigante neoportunan kodon en malsamajn dosierojn, kaj aligu signalojn al viaj komponantoj.

Sed kial?

Por la unua fojo, ni apartigos iujn niajn komponantojn en siajn proprajn QML-dosierojn. Se ni daŭre aldonas aferojn al main.qml, rapide fariĝos malfacile diri kio faras kion, kaj ni riskas ŝlimigi nian kodon.

Unue ni devas aldoni niajn novajn dosierojn al nia resources.qrc, kiun ni kreis en la unua parto de ĉi tiu lernilo.

<RCC>
    <qresource prefix="/">
        <file alias="main.qml">contents/ui/main.qml</file>
        <file alias="AddEditSheet.qml">contents/ui/AddEditSheet.qml</file>
        <file alias="KountdownDelegate.qml">contents/ui/KountdownDelegate.qml</file>
    </qresource>
</RCC>

Uzante niajn novajn dosierojn

Ni devos trovi ian manieron uzi niajn novajn dosierojn en main.qml. Feliĉe, ĉio, kion ni devas fari, estas inkluzivi deklaron de ĉi tiuj komponantoj en nia main.qml tiel:

AddEditSheet {
    id: addEditSheet
}

Etendi nian aldonan folion en aldonan/redaktan folion

Dum en la lasta lernilo ni igis nian retronombradon-aldonadon fari ion, la redakta butono sur niaj retronombraj kartoj ankoraŭ estas neaktiva. Ni ankaŭ kreis aldonan folion, kiun ni nun povus reuzigi por ankaŭ servi kiel redaktofolio... sed antaŭ ol ni atingi tion, ni devas aldoni kelkajn kromaĵojn al nia main.qml.

ĉefa.qml

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

    globalDrawer: Kirigami.GlobalDrawer {
        isMenu: true
        actions: [
            Kirigami.Action {
                text: i18n("Quit")
                icon.name: "gtk-quit"
                shortcut: StandardKey.Quit
                onTriggered: Qt.quit()
            }
        ]
    }

    ListModel {
        id: kountdownModel
    }

    // Fetches item from addEditSheet.qml and does action on signal
    AddEditSheet {
        id: addEditSheet
        onAdded: kountdownModel.append({
            "name": name,
            "description": description,
            "date": Date.parse(kdate)
        });
        onEdited: kountdownModel.set(index, {
            "name": name,
            "description": description,
            "date": Date.parse(kdate)
        });
        onRemoved: kountdownModel.remove(index, 1)
    }

    // Function called by 'edit' button on card and by 'add'-Action
    function openPopulatedSheet(mode, index = -1, listName = "", listDesc = "", listDate = "") {
        addEditSheet.mode = mode
        addEditSheet.index = index;
        addEditSheet.name = listName
        addEditSheet.description = listDesc
        addEditSheet.kdate = listDate

        addEditSheet.open()
    }

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

        // Kirigami.Action encapsulates a UI action. Inherits from Controls.Action
        actions.main: Kirigami.Action {
            id: addAction
            // Name of icon associated with the action
            icon.name: "list-add"
            // Action text, i18n function returns translated string
            text: i18nc("@action:button", "Add kountdown")
            // What to do when triggering the action
            onTriggered: openPopulatedSheet("add")
        }

        Kirigami.CardsListView {
            id: layout
            model: kountdownModel
            delegate: KountdownDelegate {}
        }
    }
}

La ŝlosilaj ŝanĝoj, kiujn ni faris, implikas la aldonon de nia kompona difino AddEditSheet (kaj KountdownDelegate pli sube) kaj novan funkcion nomitan openPopulatedSheet().

Ni trarigardu nian difinon "AddEditSheet":

AddEditSheet { 
    id: addEditSheet
    onEdited: kountdownModel.set(index, {
        name,
        description,
        date,
    });
    onAdded: kountdownModel.append({
        name,
        description,
        date,
    });
}

onAdded kaj onEdited estas signal-traktiloj. Same kiel onTriggered estas vokita kiam ni alklakas agon, ni povas uzi traktilojn kiuj respondas al niaj propraj signaloj.

AddEditSheet.qml

Rigardante nian novan AddEditSheet.qml—nian repurigitan aldonfolion—ni povas vidi kiel funkcias ĉi tiuj signaloj:

 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
import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.20 as Kirigami

// Overlay sheets appear over a part of the window
Kirigami.OverlaySheet {
    id: addEditSheet

    // Sheet mode
    property string mode: "add"

    property int index: -1
    property alias name: nameField.text
    property alias description: descriptionField.text
    property alias kdate: dateField.text

    // Signals can be read and certain actions performed when these happen
    signal added (string name, string description, var kdate)
    signal edited(int index, string name, string description, var kdate)
    signal removed(int index)

    header: Kirigami.Heading {
        // i18nc is useful for adding context for translators
        text: mode === "add" ? i18nc("@title:window", "Add kountdown") :
            i18nc("@title:window", "Edit kountdown")
    }
    // Form layouts help align and structure a layout with several inputs
    Kirigami.FormLayout {
        // Textfields let you input text in a thin textbox
        Controls.TextField {
            id: nameField
            // Provides label attached to the textfield
            Kirigami.FormData.label: i18nc("@label:textbox", "Name:")
            // Placeholder text is visible before you enter anything
            placeholderText: i18n("Event name (required)")
            // What to do after input is accepted (i.e. pressed enter)
            // In this case, it moves the focus to the next field
            onAccepted: descriptionField.forceActiveFocus()
        }
        Controls.TextField {
            id: descriptionField
            Kirigami.FormData.label: i18nc("@label:textbox", "Description:")
            placeholderText: i18n("Optional")
            onAccepted: dateField.forceActiveFocus()
        }
        Controls.TextField {
            id: dateField
            Kirigami.FormData.label: i18nc("@label:textbox", "Date:")
            inputMask: "0000-00-00"
            placeholderText: i18n("YYYY-MM-DD")
        }
        // This is a button.
        Controls.Button {
            id: deleteButton
            Layout.fillWidth: true
            text: i18nc("@action:button", "Delete")
            visible: mode === "edit"
            onClicked: {
                addEditSheet.removed(addEditSheet.index)
                close();
            }
        }
        Controls.Button {
            id: doneButton
            Layout.fillWidth: true
            text: i18nc("@action:button", "Done")
            // Button is only enabled if the user has entered something into the nameField
            enabled: nameField.text.length > 0
            onClicked: {
                // Add a listelement to the kountdownModel ListModel
                if(mode === "add") {
                    addEditSheet.added(
                        nameField.text,
                        descriptionField.text,
                        dateField.text
                    );
                }
                else {
                    addEditSheet.edited(
                        index,
                        nameField.text,
                        descriptionField.text,
                        dateField.text
                    );
                }
                close();
            }
        }
    }
}

Signaloj alvokas siajn prizorgantojn kiam ili estas vokataj. En ĉi tiu kazo, ni kreis du signalojn, "aldonitaj" kaj "redaktitaj", kiujn ni povas alvoki kun malsamaj rezultoj, kaj al kiuj ni povas kunligi informojn pri la retronombrado, kiun ni aldonas aŭ kreas. Bona afero pri signaloj estas, ke ili elmontras la variablojn difinitajn en ili al la funkcioj, kiuj ilin aŭskultas, tial ni povas simple nomi tiujn variablonomojn en niaj pritraktiloj onEdited kaj onAdded en main.qml. Niaj signaloj estas alvokitaj per la butono "Farita" depende de tio, al kio estas agordita la reĝimo propreco, difinita ĉe la supro de nia AddEditSheet.

La propreco reĝimo ankaŭ regas plurajn aliajn aferojn: ĉefe al kio estas agordita la titolo de nia folio, kaj kia teksto estas inkluzivita en niaj tekstkampoj. Tamen, defaŭlte, nia reĝimo propreco estas nur agordita por aldoni...

Kiu revenas nin al main.qml kaj nia nova funkcio openPopulatedSheet(). Vi eble rimarkis, ke tio estas kiel ĝi nomiĝas nun kiam la agonombrado-aldono estas ekigita. Ĉi tiu funkcio prenas plurajn argumentojn, kiuj estis provizitaj per defaŭltoj. Ĉi tio estas helpema kiam ni simple volas aldoni novan retronombradon, ĉar ni povas havi la koncizan funkcion vokon openPopulatedSheet("add"). Pli grave, ĉi tiu funkcio metas ĉiujn koncernajn ecojn en AddEditSheet.

function openPopulatedSheet(mode, index = -1, listName = "", listDesc = "", listDate = "") {
    addEditSheet.mode = mode
    addEditSheet.index = index;
    addEditSheet.name = listName
    addEditSheet.description = listDesc
    addEditSheet.kdate = listDate

    addEditSheet.open()
}
  • reĝimo ŝanĝas la aldoni/redakti folion depende de ĉu ĉi tiu argumento estas agordita al "aldoni" aŭ al "redakti"
  • Indekso estas necesa por ke kiam ni konservas nian redaktitan retronombradon, la ĝusta estas modifita
  • listName, listDesc, kaj listDate estas la koncernaj retronombraj detaloj kiuj devas esti enmetitaj en la kampojn de la folio

Kompreneble, por uzi nian folion por io ajn krom aldoni retronombradojn, unue ni devas funkcii la redaktan butonon sur niaj kartoj. Sed se vi rigardas nian Kirigami.CardsListView en main.qml...

Kirigami.CardsListView {
    id: layout
    model: kountdownModel
    delegate: KountdownDelegate {}
}

KountdownDelegate.qml

Ni anstataŭigis nian Kirigami.AbstractCard per delegita kompona difino de KountdownDelegate.qml.

 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
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.AbstractCard {
    id: kountdownDelegate
    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 {
                Layout.fillHeight: true
                level: 1
                text: 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
                // Column spanning within grid layout (vertically in this case)
                Layout.columnSpan: 2
                text: i18n("Edit")
                onClicked: openPopulatedSheet("edit", index, name, description, new Date(date).toISOString().slice(0,10))
            }
        }
    }
}

La onClicked propreco de la butono "Redakti" sur niaj kartoj nun nomas la funkcion openPopulatedSheet(), kun la elportitaj listelementaj propraĵoj de la karto agordita kiel la argumentoj por ĉi tiu funkcio. Per ĉi tiuj, la folio povas esti plenigita per la ĝusta teksto.

Kun tio, ni havas plene funkcian folion kie ni povas aldoni kaj redakti niajn retronombradojn!