QML: Separators between list delegates

Viewed 375

Has anyone found a good way to add separators between QML list delegates?

There is a very similar question here already but my problem is a bit more complex: How to set custom separator between items of ListView

Most of the time I use something similar as in an answer there:

ListView {
  delegate: ItemDelegate {
      ...
      Separator {
          anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
          visible: index < ListView.View.count
      }
  }
}

However, depending on the design and backend data, I don't always have a ListView/Repeater at hand and need to add manual items in a ColumnLayout instead or a mix of some items from a repeater and some manual ones:

ColumnLayout {
    ItemDelegate {
        ...
    }
    Separator {
        Layout.fillWidth: true
    }
    ItemDelegate {
        ...
    }
}

Now, both of those work but it's extremely annoying to always remember and type that separator. After lots of trying I still haven't been able to figure out a component that would take care of it.

The closest I've come to a custom Layout component like this (e.g ItemGroup.qml):

Item {
    default property alias content: layout.data

    ColumnLayout {
        id: layout
    }

    Repeater {
        model: layout.children
        delegate: Separator {
            parent: layout.children[index]
            anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
            visible: index < layout.children.length
        }
    }
}

Now this works fine for manually adding items to such a group, but again it will not work in many corner cases. For instance putting a Repeater into such an ItemGroup will create a separator for the Repeater too (given it inherits Item and thus is included in children) which results in a visual glitch with one seemingly floating separator too much...

Anyone came up with a more clever solution for this?

1 Answers

I'd try this approach:

  1. Make a custom component based on ColumnLayout.
  2. Use default property ... syntax to capture children added to it into a separate list property.
  3. Create a binding for the children property of ColumnLayout that interleaves each real child in your default property list with one of your Separators (using a Component to declare it and createObject() to create each one).

Here's a working example:

Separator.qml

import QtQuick 2.0
import QtQuick.Layouts 1.12

Rectangle {
    Layout.fillWidth: true
    height: 1
    color: "red"
}

SeparatedColumnLayout.qml

import QtQuick 2.15
import QtQuick.Layouts 1.12

ColumnLayout {
    id: layout

    default property list<Item> actualChildren

    property Component separatorComponent: Qt.createComponent("Separator.qml")

    children: {
        var result = [];
        for(var i = 0;i < actualChildren.length;++i) {
            result.push(actualChildren[i]);
            if (i < actualChildren.length - 1) {
                result.push(separatorComponent.createObject(layout));
            }
        }
        return result;
    }
}

main.qml:

import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.12

ApplicationWindow {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    SeparatedColumnLayout {
        anchors.fill: parent

        Text {
            Layout.alignment: Qt.AlignHCenter
            text: "1"
        }

        Text {
            Layout.alignment: Qt.AlignHCenter
            text: "2"
        }

        Text {
            Layout.alignment: Qt.AlignHCenter
            text: "3"
        }
    }
}

The result:

The result

Related