Separar código gigantesco em vários ficheiros e associar sinais/eventos aos seus componentes.
Mas porquê?
Pela primeira vez, iremos começar a separar alguns dos nossos componentes nos seus próprios ficheiros QML. Se continuarmos a adicionar tudo ao main.qml, irá começar a ficar cada vez mais difícil de saber o que faz cada coisa, e sujeitamo-nos a enlamear o nosso código.
Primeiro precisamos de adicionar os nossos novos ficheiros ao ficheiro resources.qrc que foi criado na primeira parte deste tutorial.
Teremos de arranjar alguma forma de usar os nossos novos ficheiros no main.qml. Felizmente, tudo o que precisamos de fazer é incluir uma declaração destes componentes no nosso main.qml, da seguinte forma:
AddEditSheet{id: addEditSheet}
Estender a nossa folha de adição para uma folha de adição/edição
Embora no último tutorial tenhamos feito com que o nosso botão de criação de contagens fizesse algo, o botão de edição continua inactivo. Também criámos uma folha de criação que poderemos agora reaproveitar para uma folha de edição... mas antes de o fazermos, precisamos de adicionar algumas coisas extra ao nosso 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{}}}}
As mudanças-chave que fizemos envolvem a adição da definição do nosso componente AddEditSheet (e o KountdownDelegate mais abaixo), assim como uma nova função chamada openPopulateSheet().
O onAdded e o onEdited são rotinas de tratamento de sinais. Assim como o onTriggered é invocado quando carregamos numa acção, também iremos usar rotinas que respondem aos nossos sinais personalizados.
AddEditSheet.qml
Se olhar para o nosso novo AddEditSheet.qml— a nossa folha de adição remodelada — podemos ver como funcionarão esses eventos:
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();}}}}
Os 'signals' (sinais ou eventos) invocam as suas rotinas de tratamento sempre que são invocados. Neste caso, criámos dois sinais, added e edited, que poderemos invocar com diferentes resultados, e aos quais poderemos associar informações sobre as contagens decrescentes que estamos a criar ou alterar. Uma coisa boa sobre os sinais é que eles expõem as variáveis definidas neles às funções que estão à escuta deles, sendo por isso que podemos invocar esses nomes de variáveis nas nossas rotinas onEdited e onAdded no main.qml. Os nossos sinais são invocados pelo botão "Terminar", dependendo do valor com que está configurada a propriedade mode, definida no topo da nossa AddEditSheet.
A propriedade mode também controla várias outras coisas: principalmente, qual o título que a nossa folha tem, qual o texto a incluir nos nossos campos de texto. Contudo, por omissão, a nossa propriedade mode está apenas configurada para add (adição)...
O que nos traz de volta ao main.qml e à nossa nova função openPopulateSheet(). Poderá ter reparado que é esta que agora é invocada quando é desencadeada a acção de adição de contagens. Esta função recebe vários argumentos que foram passados com os valores predefinidos. Isto é útil quando simplesmente queremos criar uma nova contagem decrescente, dado que podemos ter a chamada à função mais concisa openPopulateSheet("add"). Mais importante, esta função configura todas as propriedades relevantes em AddEditSheet.
O mode muda a folha de adição/edição, dependendo se este argumento está configurado como "add" ou como "edit"
O argumento index é necessário para que, quando gravarmos a nossa contagem editada, seja modificada a correcta
O listName, listDesc e listDate são os detalhes relevantes da contagem que precisam de ser colocados nos campos da folha
Obviamente, para usarmos de facto a nossa folha para algo que não seja apenas adicionar contagens, primeiro precisamos de criar o botão de edição no nosso trabalho com os cartões. Porém, se olhar para o nosso
Kirigami.CardsListView
no 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))}}}}
A propriedade
onClicked
do botão "Editar" nos nossos cartões agora invoca a função openPopulateSheet com o elemento da lista de cartões obtidos configurada como argumentos desta função. Com estes, a folha poderá ser preenchida com o texto correcto.
Com isto, já temos uma folha totalmente funciona onde poderemos adicionar e editar as nossas contagens decrescentes!