Home > Mobile >  QML Listview in ColumnLayout width management
QML Listview in ColumnLayout width management

Time:11-08

I have a ColumnLayout which contains a ListView with a Button below. Both the ListView and the Button have 'Layout.fillwidth = true' to make sure they have the same width. The width of the delegates (Labels) should occupy the full width of the Listview (to center them horizontally, and to -later on- use different background colors). If the Button is wider then the ListView, the width of the delegates should grow to still accommodate the full width of the ListView.

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Window {
    id: root
    width: 640
    height: 480
    visible: true
    title: qsTr("Layout test")

    Rectangle {
        color: 'grey'
        anchors.fill: vert_layout
    }

    ColumnLayout {
        id: vert_layout
        anchors.centerIn: parent
        spacing: 0

        ListView {
            id: list

            readonly property int itemHeight: 20
            property int visibleItems: 14

            height: visibleItems * itemHeight
            clip: true
            Layout.fillWidth: true
            Layout.minimumWidth: contentItem.childrenRect.width

            model: 20

            delegate: Label {
                id: itemlabel

                height: list.itemHeight
//                width: list.width      // <== this doesn't work
                color: 'black'
                padding: 4
                horizontalAlignment: Text.AlignHCenter
                text: "entry number "   index
            }
        }

        Button {
            id: btn

            Layout.fillWidth: true
            implicitHeight: 20

            text: "SMALL"
//            text: "REALLY WIDE BUTTON"
        }
    }
}

This code has the problem that the delegates don't occupy the full width of the ListView. Because of this the entries in the list are left aligned and not centered horizontally.

If I comment out the line that sets the width of the delegate to the width of the ListView, the entries are horizontally aligned, but the text of the entries is not fully visible anymore. Layout management doesn't seem to calculate the widths correctly anymore.

I would love to learn how to handle this properly.

CodePudding user response:

I had a number of goes at this and found this to be an interesting but frustrating problem. In the end, I felt that your logic was sound in having the ListView resize to the contents of the children. I found a trick by using a mock Item as the parent delegate for sizing the ListView. Then, inside that Item, I put a Frame and the real Label. Whilst the Item and the Label share the same size, the Frame is bigger to allow me to draw the cell Rectangle as well as to help me position the Label.

ListView {
    id: list
    Layout.minimumWidth: contentItem.childrenRect.width
    Layout.fillWidth: true
    delegate: Item {
        width: itemLabel.width
        height: list.itemHeight
        Frame {
            width: list.width
            height: parent.height
            padding: 0
            background: Rectangle {
                color: (index & 1) ? "#ccc" : "#aaa"
            }
            Label {
                id: itemLabel
                anchors.centerIn: parent
                text:  "entry number "   index
                color: "black"
            }
        }
    }
}

Here's a fully working version:

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Page {
    id: root
    width: 640
    height: 480
    visible: true
    title: qsTr("Layout test")

    Frame {
        anchors.centerIn: parent
        padding: 0
        background: Rectangle { color: "grey" }

        ColumnLayout {
            id: vert_layout
            spacing: 0

            ListView {
                id: list
                readonly property int itemPadding: 4
                readonly property int itemHeight: tm.height   itemPadding * 2
                readonly property TextMetrics tm: TextMetrics { text: "#" }
                property int visibleItems: 14
                Layout.preferredHeight: visibleItems * itemHeight
                Layout.minimumWidth: contentItem.childrenRect.width   itemPadding * 2
                Layout.fillWidth: true

                clip: true
                model: 20

                delegate: Item {
                    property string txt: wideDelegates.checked
                       ? "really wide entry number "   index
                       : "entry number "   index
                    width: itemLabel.width
                    height: list.itemHeight
                    Frame {
                        id: frame
                        width: list.width
                        height: parent.height
                        padding: 0
                        background: Rectangle {
                            color: (index & 1) ? "#ccc" : "#aaa"
                        }
                        Label {
                            id: itemLabel
                            anchors.centerIn: parent
                            text: wideDelegates.checked
                                ? "really wide entry number "   index
                               : "entry number "   index
                            color: "black"
                        }
                    }
                }
            }

            Button {
                id: btn
                Layout.fillWidth: true
                implicitHeight: 20
                text: wideButton.checked
                    ? "REALLY WIDE BUTTON"
                    : "SMALL"
            }
        }
    }

    footer: RowLayout {
        Item { Layout.fillWidth: true }
        CheckBox {
            id: wideDelegates
            text: "Wide Delegates"
        }

        CheckBox {
            id: wideButton
            text: "Wide Button"
        }
        Item { Layout.fillWidth: true }
    }
}

You can Try it Online!

  • Related