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 State
s. We define two State
s, 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 State
s is that we can easily define a Transition
to move between the two State
s 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
(alias
ed 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".