0

I am working on QML with Qt logic as backbone application and I have the following situation: at startup, QML ApplicationWindow is shown with its StatusBar, which contains two status icons and one checkable Button, named UeStaffSelector for worker login. When I press UeStaffSelector, Window named ueStaffView (it contains ListView named ueListViewWorkers) pops up and from ueListViewWorkers user is chosen for login process, as seen from following screenshot:

User List Screenshot

Now, I have another custom QML Item, named UeKeypad, which represents pin input for user, so he/she can login successfully into system. This UeKeypad must pop up after delegate is selected from ListView. The question is, where do I call UeKeypad from? From the delegate, the ListView or the ApplicationWindow? Also, how do I pass user info (user image, user name and user password) to UeKeypad?

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
KernelPanic
  • 2,328
  • 7
  • 47
  • 90

1 Answers1

2

QML bindings are cool, very cool. Instead of passing data around imperatively you can declare a direct connection between the types of your keypad and the types inside the current delegate, i.e. the current Image and user/password. In this way, as soon as a delegate is selected, the corresponding variables inside the keypad are set.

This result can be achieved using States. We define two States, a VISIBLE state for the keypad shown to the user with image/username and a HIDDEN state for the hidden keypad, when no selection is available. Using the when clause wisely, i.e. in a mutually exclusive fashion, we can ensure that the default - empty string - State is never hit and thus that the overall state of the component is always consistent.

A side effect of using States is that we can easily define a Transition to move between the two States and thus define the kind of sliding effect you are interested in. Also, by defining all the correlation in a declarative way, the form can still be edited/viewed with the designer. That would be impossible with imperative code.

Access to the actual values inside the current delegate can be done via the values inside the currentItem (aliased if not available in the top level type) or via the model get. Note the usage of index, an attached property of both Repeater and ListView.

The following example summarises this approach, also providing both ways of accessing ListView data.

import QtQuick 2.0
import QtQuick.Controls 1.3
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2

ApplicationWindow {
    width: 600
    height: 800
    visible: true

    Column {
        id: keypad
        x: parent.width - width     // put in the corner
        z: 2
        Row {
            width: 200
            spacing: 10
            Image {
                id: keyImage
                width: 100
                height: 100
            }
            Text {
                id: keyText
                anchors.verticalCenter: parent.verticalCenter
                font.pixelSize: 30
            }
        }

        Grid{
            columns: 3
            columnSpacing: 5
            rowSpacing: 5
            Repeater {
                model: 9
                Button {
                    text: index + 1
                    onClicked: console.info(text + " " + keyText.text)
                }
            }
        }

        states: [
            State {
                name: "HIDDEN"
                PropertyChanges {
                    target: keypad
                    y: keypad.parent.height
                }
                when: list.currentIndex < 0
            },
            State {
                name: "VISIBLE"
                PropertyChanges {
                    target: keypad
                    y: keypad.parent.height / 2 - keypad.height / 2
                }
                PropertyChanges {
                    target: keyImage
                    source: list.model.get(list.currentIndex).image//list.currentItem.imagePath
                }
                PropertyChanges {
                    target: keyText
                    text: list.model.get(list.currentIndex).user//list.currentItem.label
                }
                when: list.currentIndex >= 0
            }
        ]

        transitions: Transition {
            NumberAnimation {
                properties: "y";
                duration: 1000
                easing.type: Easing.InQuad
            }
        }
    }


    ListView {
        id: list
        anchors.fill: parent
        currentIndex: -1
        model: ListModel{
            ListElement {
                image: "http://www.theapricity.com/forum/image.php?u=9098&dateline=1442619767"
                user: "first"
            }

            ListElement {
                image: "http://a.dilcdn.com/bl/wp-content/uploads/sites/8/2013/10/angry-cat-200x200.jpg"
                user: "second"
            }

            ListElement {
                image: "http://images.all-free-download.com/images/graphicthumb/walking_sand_cat_516744.jpg"
                user: "third"
            }
        }

        delegate: Rectangle {
            width: ListView.view.width
            height: 220
            property alias imagePath: img.source
            property alias label: label.text
            RowLayout {
                anchors.fill: parent
                Image {
                    id: img
                    width: 100
                    height: 100
                    fillMode: Image.PreserveAspectFit
                    source: image
                    Layout.alignment: Qt.AlignLeft
                    Layout.preferredWidth: 100
                }

                Text {
                    id: label
                    Layout.fillWidth: true
                    Layout.alignment: Qt.AlignCenter
                    text: user
                    font.pixelSize: 30
                }
                MouseArea{
                    anchors.fill: parent
                    onClicked: list.currentIndex = index   //set the current item
                }
            }
            color: ListView.isCurrentItem  ? "steelblue" : "transparent"
            scale: ListView.isCurrentItem ? 1 : 0.7
        }
    }
}

This approach has clearly many advantages over the imperative one, as we discussed above. Mostly because is the more natural way to define a (sort of) execution workflow in QML. You can still (and sometimes more easily) define imperative code for the purpose. Beware that, in most cases, if you are following the imperative way, you are simply "doing it wrong".

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82