Reputation: 9701
I'm following this tutorial (without the flickable content in each entry) for Qt 4.8 while using Qt 5.7 with QtQuick 2.0
. The way the ListView
there works is as follows:
Close
button in detailed view to reset the state of entry to its default compact view.This leads to a clutter where at some point if the user clicks on all items in which case all will be shown in their full view. Having the user click on the Close
button every time he/she opens a detailed view also is (omho) not that handy.
I've altered the entry to close when the user clicks on the view. I'm also trying to prevent this clutter and achieve a more (omho) flowing behaviour:
Currently I'm looping through my ListView
's contentItem.children[loop_index]
and setting the state to ""
("Details"
= show detailed view | ""
= show compact view). Due to the way ListView
works (loading/unloading delegates on demand) this is quite unreliable and I often get an undefined reference when I try to access the state of other delegates. The following MouseArea
, which I'm using to do all that, is part of every delegate:
// state is a QML `State` that is bound to the delegate (see below for the details on it)
MouseArea {
anchors.fill: background
onClicked: {
// Iterate through all other entries and close them
for (var entry = 0; entry < listView.count; ++entry) {
if(listView.contentItem.children[entry] !== gestureEntry) {
console.log("Hide other element");
listView.contentItem.children[entry].state = ""; // IT FAILS HERE (SOMETIMES)
}
}
// Change view of current entry
if(gestureEntry.state === "Details") {
gestureEntry.state = "";
console.log("Hiding details")
}
else {
gestureEntry.state = "Details";
console.log("Showing details");
}
}
}
with state
being a delegate's state:
states: State {
name: "Details"
PropertyChanges { target: background; color: "white" }
PropertyChanges { target: gestureImage; width: 130; height: 130 } // Make picture bigger
PropertyChanges { target: gestureEntry; detailsOpacity: 1; x: 0; y: 0 } // Make details visible
PropertyChanges { target: gestureEntry; height: listView.height } // Fill the entire list area with the detailed view
}
I'm thinking that the state
information can be stored inside the ListModel
itself making it possible to iterate through the model's contents (which are always there unlike the contents of the delegates) however I don't know how to automatically update my list (and the currently visible/invisible delegates) when an entry changes in the model. From what I've found so far it seems not possible to do that since the ListView
doesn't actively monitor its ListModel
.
Is this indeed the case? If yes, then is it possible to go around this problem in a different way?
Upvotes: 4
Views: 6417
Reputation: 379
guess it is an issue because you stored a state in your delegate. You should not do this as described in the delegate
-property (Link), because the delegates get reused when they get out of view.
At least you should use a when: ListView.isCurrentItem
in the State and depend on a value of the ListView. So only your current delegate is maximized. Then in the MouseArea only set `ListView.view.currentIndex = index'. Don't change the state manually in the function!
I ran in the same trouble, removed the states completely and just used the attached property ListView.isCurrentItem
. But binding the state to a Value from the ListView should also work, because it's not stored in the delegate.
Minimal example:
import QtQuick 2.0
Item {
width: 800
height: 600
ListView {
id: view
anchors.fill: parent
model: 3
spacing: 5
currentIndex: -1
delegate: Rectangle {
id: delegate
color: ListView.isCurrentItem ? "lightblue" : "green" // directly change properties depending on isCurrentItem
height: 100
width: 100
states: State {
name: "maximized"
when: delegate.ListView.isCurrentItem // bind to isCurrentItem to set the state
PropertyChanges {
target: delegate
height: 200
}
}
MouseArea {
anchors.fill: parent
//onClicked: delegate.ListView.view.currentIndex = model.index // if only selection is wanted
onClicked: {
//console.debug("click");
if (delegate.ListView.isCurrentItem)
{
delegate.ListView.view.currentIndex = -1;
}
else
{
delegate.ListView.view.currentIndex = model.index;
}
}
}
Text {
anchors.centerIn: parent
text: index
}
}
Text {
text: "CurrentIndex: " + parent.currentIndex
}
}
}
Upvotes: 2
Reputation: 5336
Why don't you use the currentIndex
property of your ListView
?
Just modify your delegate like this:
Item {
id: gestureEntry
...
state: ListView.isCurrentItem?"Details":""
...
MouseArea {
anchors.fill: background
onClicked: {
if(listView.currentIndex == index)
listView.currentIndex = -1
else
listView.currentIndex = index
}
}
}
EDIT:
The only issue with the solution above is that - upon loading - an entry in the ListView
is preselected which automatically triggers the detailed view of that entry. In order to avoid that the following needs to be added to listView
:
Component.onCompleted: {
listView.currentIndex = -1;
}
This ensures that no entry will be preselected.
Upvotes: 2