Expandable ListView delegate and other questions



  • Hello all

    This is my first question in this subforum/category and I'm afraid it won't be last since I'm just starting with QML/Quick.

    I've written simple example with which I'm having problems (explanations below):

    import QtQuick 2.7
    import QtQuick.Controls 2.1
    import QtQuick.Layouts 1.0
    
    ApplicationWindow {
        id: app
        visible: true
        width: 340
        height: 480
        title: qsTr("Simple test")
    
    
        Component {
            id: delComp
            Rectangle {
                width: view.width
                height: 80
                border.color: "red"
                border.width: 1
    
                Row {
                    id: row
                    anchors {
                        top: parent.top
                        horizontalCenter: parent.horizontalCenter
                    }
                    Rectangle {
                        width: 100
                        height: 40
                        color: "lightgray"
                        Text {
                            anchors.centerIn: parent
                            text: value
                        }
                    }
                    CheckBox {
                        text: "Active"
                        checked: active
                        onCheckedChanged:  {
                            active = checked
                        }
                    }
                    Text {
                        text: "\u2630"
                        font.pixelSize: 20
                        anchors.verticalCenter: parent.verticalCenter
                        MouseArea {
                            anchors.fill: parent
                            onClicked: {
                                console.log("Changing visible:" + details.visible)
                                details.visible = !details.visible
    //                            console.log("Opening popup")
    //                            schedDialog.row = index
    //                            schedDialog.auto = auto
    //                            schedDialog.open()
                            }
                        }
                    }
                }
    
                Rectangle {
                    id: details
                    visible: false
                    anchors {
                        top: row.bottom
                        bottom: parent.bottom
                        horizontalCenter: parent.horizontalCenter
                    }
                    height: 80
                    Text { text: "Details" }
                }
    
                Connections {
                    target: schedDialog
                    onAccepted: {
                        if (index === schedDialog.row) {
                            console.log("Delegate accept")
                            auto = schedDialog.auto
                        }
                    }
                }
            }
        }
    
        Dialog {
            id: schedDialog
            x: (parent.width - implicitWidth)/2
            y: (parent.height - implicitHeight)/2
            standardButtons: Dialog.Cancel | Dialog.Ok
            modal: true
            property int row: 0
            property alias auto: chkBox.checked
    //        property int start: 0
    //        property int stop: 0
    
            ColumnLayout {
                CheckBox {
                    id: chkBox
                    text: "Enable schedule"
                }
            }
    //        onAccepted: {
    //            // XXX - how can I update model here???
    //        }
        }
    
        ListView {
            id: view
            anchors.fill: parent
            model: simpleModel
            delegate: delComp
        }
    
        ListModel {
            id: simpleModel
            ListElement {
                value: 1023
                active: true
                auto: false
                start: 0 // TODO: What is QML equivalent of QTime
                stop: 0
            }
            ListElement {
                value: 0
                active: false
                auto: true
                start: 10
                stop: 20
            }
            ListElement {
                value: 5421
                active: true
                auto: true
                start: 1000
                stop: 2020
            }
            onDataChanged: {
                console.log("Data changed " + topLeft)
            }
        }
    }
    

    So this is a simple list view with some items and I want to show each item with simplified interface (not showing all details). This is quite simple and I've added "heaven" button which originally (now commented) was opening popup dialog with the remaining details for given item.

    First question(s):

    • if I have Dialog as part of delegate then there is no problem with access/modification of model's data but how can I position dialog popup from delegate relative to the app window (and not relative to the delegate) - setting y to (app.height - implicitHeight)/2 seems to position dialog correct distance but not from app.top but from the delegate.top - if I get this then below questions are only for my curiosity
    • if the dialog is outside of the delegate how can I access given model item data, any attached properties, modelData? Right now I copy values to Dialog.
    • if the dialog is outside of the delegate what would be the best method to update model - right now I have all delegates connected to the Dialog's accepted signal and they check row in order to see if they need to update model with Dialog's values or not

    Since I had problems with this dialog I thought about having different design and now I want to create list view in which delegate will be expanding a bit and showing hidden content. This is not really similar to "expandingdelegates" from the Qt Quick 'Views' example coming with Qt. In there the delegate expands to the whole screen and I want to have delegate expand a bit showing more controls and pushing other delegates down.
    I thought about adding second Rectangle (details) and changing its visibility but that does not work. The "Details" text visibility is changing as needed but the delegate is not sizing according to its visibility.

    How can I achieve that effect?

    For example setting height of the top level Rectangle in delComp to: details.visible ? row.height + details.height : row.height does not do anything. Is it possible to have list view with each delegate having different height? If so how can I achieve that?

    My apologies for a lengthy post, and thank you for reading it.

    Best regards
    Andrzej



  • I've just found out that if instead of specifying height of the top level Rectangle in delComp to:

      height: details.visible ? row.height + details.height : row.height
    

    I do it as:

      height: row.height + (details.visible ? details.height : 0)
    

    then the delegate is expanding as I wanted. Could somebody explain the difference?

    Best regards
    Andrzej



  • This post is deleted!


  • I'm still interested in answer to my questions (anybody?) but since I now have the code working as I want I'm marking this topic as solved.



  • @AndrzejO said in Expandable ListView delegate and other questions:

    • if I have Dialog as part of delegate then there is no problem with access/modification of model's data but how can I position dialog popup from delegate relative to the app window (and not relative to the delegate) - setting y to (app.height - implicitHeight)/2 seems to position dialog correct distance but not from app.top but from the delegate.top

    You must translate the coordinates. See Item's mapTo... and mapFrom... methods. They are difficult to use so I don't even try to give any code. Having one dialog per delegate object is of course inefficient, one dialog is better.

    You don't access a delegate's properties from outside that delegate object. Instead you communicate through the model or set things from the delegate. Here it's done through the model, modifying your code:

    MouseArea {
                            anchors.fill: parent
                            onClicked: {
                                schedDialog.row = index
                                schedDialog.open()
                            }
                        }
    
    Dialog {
            id: schedDialog
            x: (parent.width - implicitWidth)/2
            y: (parent.height - implicitHeight)/2
            standardButtons: Dialog.Close
            modal: true
            property int row: 0
            ColumnLayout{
                CheckBox {
                    id: chkB
                    text: "Enable schedule"
                }
            }
            onOpened: chkB.checked = simpleModel.get(row).auto
            onClosed: simpleModel.get(row).auto = chkB.checked // or use onCheckedChanged of chkB
        }
    

    Basically you use whatever means are available for manipulating the model data. ListModel has this get() method which lets you handle one item as a javascript object. C++ models have their own methods.

    I've just found out that if instead of specifying height of the top level Rectangle in delComp to:
    height: details.visible ? row.height + details.height : row.height
    I do it as:
    height: row.height + (details.visible ? details.height : 0)
    then the delegate is expanding as I wanted. Could somebody explain the difference?

    At least I can't. Very interesting.



  • Thanks @Eeli-K for taking a look at it.
    Would your onClosed work the same if the model was on the C++ side? I mean if the model is defined in C++ and e.g. set as a context property could you call model.get(row).auto = chkB.checked on it, or is it just the interface of ListModel QML type. If the latter how would you define function with Q_INVOKED that would provide such interface?

    Best regards
    Andrzej



  • @AndrzejO As I said, "C++ models have their own methods". In the documentation of QAbstractItemModel you find insert..., move..., remove... and set... methods or you can implement your own custom interface as long as you send the corresponding signals which tell the listeners when the model is changed. For example you have Q_INVOKABLE changeSomeDataValueOnEveryRow(int argumentForCalculatingNewValues). You change all wanted data values there and choose the signal to send - dataChanged for each row, or maybe even modelAboutToBeReset and modelReset. Learn how to subclass QAbstractItemModel.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.