Skip to content

Instantly share code, notes, and snippets.

@Iktwo
Last active February 12, 2022 06:30
Show Gist options
  • Save Iktwo/9cc40a41acfbbe890762edd091cc5900 to your computer and use it in GitHub Desktop.
Save Iktwo/9cc40a41acfbbe890762edd091cc5900 to your computer and use it in GitHub Desktop.
ExpandableListView
import QtQuick 2.0
// Use Item as root element to control what properties are exposed, without this the delegate could be overwritten and the list would not work as expected
Item {
id: root
// Expose ListView properties
property alias model: listView.model
property alias clip: listView.clip
// Property to control wether or not the list can expand
property bool expandable: true
// Property to control if the list should read the initial model and expand the elements
property bool autoExpanded: true
// Property to control if interactive property should be set to false automatically
property bool autoDisableInteractiveProperty: true
// Property that defines the component to render when the element is compressed
property Component compressedItem
// Property that defines the component to render when the element is expanded
property Component expandedItem
ListView {
id: listView
anchors.fill: parent
delegate: Component {
Loader {
property var view: ListView.view
property int itemIndex: index
property var dataSource: {
try {
if (modelData !== undefined) {
return modelData
} else {
return model
}
} catch(e) {
return model
}
}
property var expand: function expanded(value) {
// Check if the list expandable, otherwise do nothing (■_■¬)
if (root.expandable) {
var expanded = itemInternal.expanded
// If a value is provided use that, otherwise toggle the current state
if (value) {
expanded[index] = value
} else {
expanded[index] = !expanded[index]
}
itemInternal.expanded = expanded
}
}
sourceComponent: {
var item = itemInternal.expanded[index] ? expandedItem : compressedItem
try {
if (modelData !== undefined && modelData.children !== undefined) {
return item
} else if (model.children !== undefined){
return item
} else {
return compressedItem
}
} catch(e) {
if (model !== undefined && model.children !== undefined) {
return item
} else {
return compressedItem
}
}
}
onLoaded: {
if (autoDisableInteractiveProperty && sourceComponent === expandedItem) {
// If the loaded Component is a Flickable the interactive property needs to be disabled for the list to scroll properly
try {
item.interactive = false
} catch(e) {
}
}
}
}
}
// 'Private' internal item that holds expanded information
Item {
id: itemInternal
property var expanded: []
}
onCountChanged: {
// Check if the list is supposed to auto expand, otherwise do nothing (■_■¬)
if (autoExpanded) {
var expanded = []
for (var i = 0; i < count; ++i) {
if (model[i] !== undefined && model[i].expanded !== undefined) {
expanded.push(model[i].expanded)
} else if (model.get(i).expanded !== undefined){
expanded.push(model.get(i).expanded)
} else {
expanded.push(false)
}
}
itemInternal.expanded = expanded
}
}
}
}
import QtQuick 2.11
import QtQuick.Window 2.11
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property var sourceData: [
{
"title": "item1",
"expanded": false,
"children": [{
"title": "subtitle1"
},
{
"title": "subtitle2"
},
{
"title": "subtitle3"
}
]
}, {
"title": "title2",
"expanded": false
},
{
"title": "title3",
"expanded": false
},
{
"title": "title4",
"expanded": true,
"children": [{
"title": "subtitle1"
},
{
"title": "subtitle2"
},
{
"title": "subtitle3"
},
{
"title": "subtitle3"
}
]
},
{
"title": "title5",
"expanded": false
}
]
ListModel {
id: fruitModel
ListElement {
title: "Apple"
cost: 2.45
}
ListElement {
title: "Orange"
cost: 3.25
}
ListElement {
title: "Banana"
cost: 1.95
expanded: true
children: [ListElement {
title: "Inner Banana"
cost: 1.95
expanded: true
},
ListElement {
title: "Inner Banana 2"
cost: 1.95
expanded: true
}]
}
}
ExpandableListView {
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
width: parent.width / 2
model: sourceData
compressedItem: Rectangle {
width: view.width
height: view.height / 5
color: "#3498db"
border {
color: "black"
width: 1
}
Text {
anchors.centerIn: parent
text: itemIndex + " - " + dataSource.title
}
MouseArea {
anchors.fill: parent
onClicked: {
expand()
}
}
}
expandedItem: ListView {
height: count * 100
width: view.width
model: dataSource.children
delegate: Rectangle {
color: "#56bafd"
border {
color: "black"
width: 1
}
height: 100
width: ListView.view.width
Text {
anchors.centerIn: parent
text: itemIndex + " - " + modelData.title
color: "red"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
expand()
}
}
}
}
ExpandableListView {
anchors {
right: parent.right
top: parent.top
bottom: parent.bottom
}
model: fruitModel
width: parent.width / 2
autoExpanded: true
compressedItem: Rectangle {
width: view.width
height: view.height / 5
color: "#3498db"
border {
color: "black"
width: 1
}
Text {
anchors.centerIn: parent
text: itemIndex + " - " + dataSource.title
}
MouseArea {
anchors.fill: parent
onClicked: {
expand()
}
}
}
expandedItem: ListView {
height: count * 100
width: view.width
model: dataSource.children
delegate: Rectangle {
color: "#56bafd"
border {
color: "black"
width: 1
}
height: 100
width: ListView.view.width
Text {
anchors.centerIn: parent
text: itemIndex + " - " + model.title
color: "red"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
expand()
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment