Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

ListView width inside Repeater



  • Hi all. I am trying to build custom table, that fit to my needs. This is the code:

    GridLayout {
            property var columnName:  ["index", "name", "state", "time", "pps", "format"]
            property var columnWidth: [0.1,     0.4,    0.1,     0.1,    0.1,   0.2]
    
            id: grid
            anchors.fill: parent
            flow: GridLayout.LeftToRight
            columns: columnName.length
            rows: 5
    
            Repeater {
                id: repeater
                model: grid.columnName
    
                ListView {
                    property int prevCurrentIndex: 0
    
                    anchors.fill: parent
                    focus: index === 0
                    model: refTableModel
                    clip: true
                    implicitWidth: contentItem.childrenRect.width
    
                    delegate: Text {
                        width: 40
                        text: model[modelData]
                    }
                    onCurrentIndexChanged: {
                        if (focus !== true)
                            return;
    
                        var dir = currentIndex - prevCurrentIndex;
                        console.log("direction: " + dir);
                        for (var idx = 0; idx < repeater.count; ++idx) {
                            var item = repeater.itemAt(idx);
                            if (item !== this && item !== null) {
                                if (dir > 0) {
                                    item.incrementCurrentIndex();
                                } else {
                                    item.decrementCurrentIndex();
                                }
                            }
                        }
                        prevCurrentIndex = currentIndex;
                    }
                }
            }
        }
    

    What i want is to setup width of each column (implemented with ListView) accordingly to coefficient, denoting the part of whole GridLayout width. But this width somehow equal to zero, and i don't understand why. How to achieve what i needs?



  • @egor.utsov
    Something like this?

    ApplicationWindow {
        id: window
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        GridLayout {
            id: grid
            property var columnName:  ["index", "name", "state", "time", "pps", "format"]
            property var columnWidth: [0.1,     0.4,    0.1,     0.1,    0.1,   0.2]
    
            anchors.fill: parent
            flow: GridLayout.LeftToRight
            columns: columnName.length
            rows: 1
    
            Repeater {
                id: repeater
                model: grid.columnName
    
                ListView {
                    id: list
                    model: 10
                    clip: true
    
                    width: grid.columnWidth[index]*window.width
                    height: window.height
    
                    delegate: Text {
                        width: 40
                        text: "TEST" + index
                    }
                }
            }
        }
    


  • @Diracsbracket Thanks! It approach worked, but i am planned to put this table in separate QML file and use it as a component. So i don't have access to ApplicationWindow object. What to do in this case. Use root item of QML document instead of ApplicationWindow?



  • @Diracsbracket I checked approach with separate QML doc and it is not working. I try to create table as

    Window {
      width: 256
      height: 64
    
      CustomTable {
        width: parent.width
        height: parent.height
        model: Model{}
      }
    }
    

    Here, width and height of root item inside CustomTable will be equal to zero at the moment, when ListViews created. But if i create CustomTable like this:

    CustomTable {
        width: 256
        height: 64
        model: Model{}
      }
    

    it will work. But i don't want to depends on absolute values. How can i deal with it? And why sizes not calculated before constructing child items?



  • @egor.utsov
    You can define two properties

        Component {
            id: gridComponent
    
            GridLayout {
                id: grid
                property var columnName:  ["index", "name", "state", "time", "pps", "format"]
                property var columnWidth: [0.1,     0.4,    0.1,     0.1,    0.1,   0.2]
    
                property real gridWidth: 10 //dummy value
                property real gridHeight: 10 //dummy value
                .....
    
                    ListView {
                        id: list
                         ...
                        width: grid.columnWidth[index]*grid.gridWidth
                        height: grid.gridHeight
                        ...
                    }
        }
    }//Component
    

    and then in JS for example:

        Component.onCompleted: {
            var myGrid = gridComponent.createObject(someParent, {"gridWidth": someValue, "gridHeight": someValue2})
        }
    


  • @Diracsbracket Where i should catch onCompleted event ? Inside component? Below is not working

    CustomComponent.qml:

    Item {
      id: root
      Component.onCompleted {console.log("width = " + root.width);} // width = 0 <-- problem
    }
    

    Main.qml

    Window {
      width: 256
      height: 64
      CustomComponent {
        width: parent.width
        height: parent.height
      }
    }
    


  • @egor.utsov
    If you use the parent's width and height properties to set a child's dimensions, then the child's width and height are set when the child first becomes visible, and this is done after Component.onCompleted() it seems - the parent seems to be assigned after the component is completed.

    So the console.debug() in your particular example shows the still uninitialized value.



  • @Diracsbracket what to do then?



  • @egor.utsov
    For example:

            Item {
                anchors.fill: parent
                id: item
                property bool ok: (height>0) && (width>0)
    
                onOkChanged: {
                    console.debug(item.height + " " + item.width)
                    var comp = Qt.createComponent("CustomComponent.qml")
                    var obj = comp.createObject(this, {"gridWidth": item.width, "gridHeight": item.height})
                }
    
            }
    


  • Thanks to @Diracsbracket

    Final solution:

    Item {
        property ListModel model
        property bool timeToBuildTable: (width > 0) && (height > 0)
    
        id: root
    
        onTimeToBuildTableChanged: {
            if (timeToBuildTable) {
                loader.anchors.fill = root;
                loader.sourceComponent = gridComponent;
            }
        }
    
        onActiveFocusChanged: loader.item.forceActiveFocus();
    
        Loader {
            id: loader
            anchors.fill: parent
        }
    
        Component {
            id: gridComponent
    
            FocusScope {
                GridLayout {
                    property var columnName:  ["index", "name", "state", "time", "pps", "format"]
                    property var columnWidth: [0.1,     0.5,    0.1,     0.1,    0.1,   0.1]
    
                    id: grid
                    anchors.fill: parent
    
                    flow: GridLayout.LeftToRight
                    columns: columnName.length
                    rows: 5
                    rowSpacing: 1
                    columnSpacing: 1
    
                    Repeater {
                        id: repeater
                        model: grid.columnName
    
                        ListView {
                            property int columnIndex: index
                            property int prevCurrentIndex: 0
    
                            id: column
    
                            model: root.model
                            focus: index === 0
                            clip: true
                            spacing: 1
    
                            Layout.fillHeight: true
                            Layout.fillWidth: true
                            width: root.width * grid.columnWidth[columnIndex]
    
                            delegate: Rectangle {
                                width: root.width * grid.columnWidth[column.columnIndex]
                                height: root.height / grid.rows
    
                                Text {
                                    text: model[modelData]
                                }
                            }
    
                            onCurrentIndexChanged: {
                                if (focus !== true)
                                    return;
    
                                var dir = currentIndex - prevCurrentIndex;
                                for (var idx = 0; idx < repeater.count; ++idx) {
                                    var item = repeater.itemAt(idx);
                                    if (item !== this && item !== null) {
                                        if (dir > 0) {
                                            item.incrementCurrentIndex();
                                        } else {
                                            item.decrementCurrentIndex();
                                        }
                                    }
                                }
                                prevCurrentIndex = currentIndex;
                            }
                        }
                    }
                }
            }
        }
    }
    
    

Log in to reply