Separera tungrodd kod i olika filer, och koppla signaler till dina komponenter.
Men varför?
För första gången separerar vi vissa av våra komponenter i sina egna QML-filer. Om vi fortsätter att lägga till saker i main.qml, blir det snart svårt att säga vad som gör vad, och vi riskerar att trassla till vår kod.
Först måste vi lägga till våra nya filer i vår resources.qrc som vi skapade i den första delen av handledningen.
Vi behöver komma på något sätt att använda våra nya filer i main.qml. Som tur är behöver vi bara inkludera en deklaration av komponenterna i vår main.qml på följande sätt:
AddEditSheet{id: addEditSheet}
Utöka vårt tilläggsblad till ett tilläggs- och redigeringsblad
Medan vi fick vår knapp för tillägg av nedräknare att göra någonting i den förra handledningen, är redigeringsknappen på våra nedräkningskort fortfarande inaktiv. Vi skapade också ett tilläggsblad som vi nu skulle kunna återanvända för att också fungera som ett redigeringsblad ... men innan vi kommer till det, behöver vi lägga till några extra saker i vår main.qml.
importQtQuick2.15importQtQuick.Controls2.15asControlsimportQtQuick.Layouts1.15importorg.kde.kirigami2.20asKirigamiKirigami.ApplicationWindow{id: roottitle:i18nc("@title:window","Day Kountdown")globalDrawer:Kirigami.GlobalDrawer{isMenu:trueactions:[Kirigami.Action{text:i18n("Quit")icon.name:"gtk-quit"shortcut:StandardKey.QuitonTriggered:Qt.quit()}]}ListModel{id: kountdownModel}// Fetches item from addEditSheet.qml and does action on signal
AddEditSheet{id: addEditSheetonAdded: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
functionopenPopulatedSheet(mode,index=-1,listName="",listDesc="",listDate=""){addEditSheet.mode=modeaddEditSheet.index=index;addEditSheet.name=listNameaddEditSheet.description=listDescaddEditSheet.kdate=listDateaddEditSheet.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: layoutmodel:kountdownModeldelegate:KountdownDelegate{}}}}
The key changes we have made involve the addition of our component definition AddEditSheet (and KountdownDelegate further down) and a new function called openPopulatedSheet().
Låt oss gå igenom definitionen av vårt AddEditSheet:
onAdded and onEdited are signal handlers. Just like onTriggered is called when we click an action, we can use handlers that respond to our custom signals.
AddEditSheet.qml
Looking at our new AddEditSheet.qml—our repurposed adding sheet—we can see how these signals work:
importQtQuick2.15importQtQuick.Controls2.15asControlsimportQtQuick.Layouts1.15importorg.kde.kirigami2.20asKirigami// Overlay sheets appear over a part of the window
Kirigami.OverlaySheet{id: addEditSheet// Sheet mode
propertystringmode:"add"propertyintindex:-1propertyaliasname:nameField.textpropertyaliasdescription:descriptionField.textpropertyaliaskdate:dateField.text// Signals can be read and certain actions performed when these happen
signaladded(stringname,stringdescription,varkdate)signaledited(intindex,stringname,stringdescription,varkdate)signalremoved(intindex)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: descriptionFieldKirigami.FormData.label:i18nc("@label:textbox","Description:")placeholderText:i18n("Optional")onAccepted:dateField.forceActiveFocus()}Controls.TextField{id: dateFieldKirigami.FormData.label:i18nc("@label:textbox","Date:")inputMask:"0000-00-00"placeholderText:i18n("YYYY-MM-DD")}// This is a button.
Controls.Button{id: deleteButtonLayout.fillWidth:truetext:i18nc("@action:button","Delete")visible:mode==="edit"onClicked:{addEditSheet.removed(addEditSheet.index)close();}}Controls.Button{id: doneButtonLayout.fillWidth:truetext:i18nc("@action:button","Done")// Button is only enabled if the user has entered something into the nameField
enabled:nameField.text.length>0onClicked:{// 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();}}}}
Signals invoke their handlers when they are called. In this case, we have created two signals, added and edited, that we can invoke with different outcomes, and to which we can attach information about the countdown we are adding or creating. A neat thing about signals is that they expose the variables defined in them to the functions that are listening to them, which is why we can just call those variable names in our onEdited and onAdded handlers in main.qml. Our signals are invoked by the "Done" button depending on what the mode property, defined at the top of our AddEditSheet, is set to.
Egenskapen mode styr också flera andra saker: i huvudsak vad bladets titel ställs in till, och vilken text som ska inkluderas i våra textfält. Dock är egenskapen mode bara inställd att lägga till ...
Which brings us back to main.qml and our new openPopulatedSheet() function. You might have noticed that this is what it is called now when the countdown-adding action is triggered. This function takes in several arguments which have been provided with defaults. This is helpful when we simply want to add a new countdown, because we can have the concise function call openPopulatedSheet("add"). More importantly, this function sets all the relevant properties in AddEditSheet.
mode ändrar tilläggs- och redigeringsbladet beroende på om argumentet är tilldelat "add" eller "edit"
index is needed so that when we save our edited countdown, the correct one is modified
listName, listDesc och listDate är den relevanta nedräkningsinformationen som måste lägga till i bladets fält
Of course, to actually use our sheet for anything besides adding countdowns first we need to make the edit button on our cards work. But if you look at our
Kirigami.CardsListView
in main.qml...
importQtQuick2.15importQtQuick.Controls2.15asControlsimportQtQuick.Layouts1.15importorg.kde.kirigami2.20asKirigamiKirigami.AbstractCard{id: kountdownDelegatecontentItem:Item{implicitWidth:delegateLayout.implicitWidthimplicitHeight:delegateLayout.implicitHeightGridLayout{id: delegateLayoutanchors{left:parent.lefttop:parent.topright:parent.right}rowSpacing:Kirigami.Units.largeSpacingcolumnSpacing:Kirigami.Units.largeSpacingcolumns:root.wideScreen?4:2Kirigami.Heading{Layout.fillHeight:truelevel:1text:i18n("%1 days",Math.round((date-Date.now())/86400000))}ColumnLayout{Kirigami.Heading{Layout.fillWidth:truelevel:2text:name}Kirigami.Separator{Layout.fillWidth:truevisible:description.length>0}Controls.Label{Layout.fillWidth:truewrapMode:Text.WordWraptext:descriptionvisible:description.length>0}}Controls.Button{Layout.alignment:Qt.AlignRight// Column spanning within grid layout (vertically in this case)
Layout.columnSpan:2text:i18n("Edit")onClicked:openPopulatedSheet("edit",index,name,description,newDate(date).toISOString().slice(0,10))}}}}
The
onClicked
property of the "Edit" button on our cards now calls the openPopulatedSheet() function, with the card's fetched list element properties set as the arguments for this function. With these, the sheet can be populated with the correct text.
Därmet har vi ett fullständigt fungerande blad där vi kan lägga till och redigera våra nedräkningar.