Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flow layout with centered content

Tags:

qt5

qml

qtquick2

I have a row with items which should stack when the window width gets too small for displaying all items in a row, as shown in the following sketch:

enter image description here

The Flow component stacks the items but they are not centered but aligned on the left or right side:

Flow {
    Item {}
    Item {}
    Item {}
    Item {}
    Item {}
}

Is there a built-in way in QML to make the flow centered?

like image 822
Hyndrix Avatar asked Nov 01 '15 12:11

Hyndrix


4 Answers

Well there is no built-in way but I found a workaround to do it.

The idea is simple, since Flow is already an Item it has anchors.leftMargin and anchors.rightMargin. So if we can calculate, how many elements is inside the row of theFlow then we are able to calculate the left and right margins. So we can center in.

Here it is a simple code,

        Flow {
        property int rowCount: parent.width / (elements.itemAt(0).width + spacing)
        property int rowWidth: rowCount * elements.itemAt(0).width + (rowCount - 1) * spacing
        property int mar: (parent.width - rowWidth) / 2

        anchors {
            fill: parent
            leftMargin: mar
            rightMargin: mar
        }

        spacing: 6
        Repeater {
            id: elements
            model: 5
            Rectangle {
                color: "#aa6666"
                width: 100; height: 100
            }
        }
like image 74
cavitsinadogru Avatar answered Oct 22 '22 05:10

cavitsinadogru


CenterFlow.qml

import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.3

Rectangle{
    implicitHeight: elements.height
    default property alias content: elements.children
    property int flowSpacing: 0
    color: "transparent"
    Flow{
        function mar(){
            var rowCount = parent.width / (elements.children[0].width + flowSpacing);
            if(rowCount> elements.children.length){
                rowCount = elements.children.length
            }
            rowCount = parseInt(rowCount)
            var rowWidth = rowCount * elements.children[0].width + (rowCount - 1) * flowSpacing
            print(elements.height)
            return (parent.width - rowWidth) / 2

        }
        spacing: flowSpacing
        id:elements
        leftPadding: mar()
        rightPadding: mar()
        width: parent.width
    }
}

Use it like this:

CenterFlow {
     id:centerFlow
     flowSpacing: 10
     width: parent.width -10*2

     Button{

     }
     Button{

    }
}
like image 26
晋立宪 Avatar answered Oct 22 '22 05:10

晋立宪


Another very similar use case is to have the initial, smaller flow horizontally centred as well:

import QtQuick 2.0
import QtQuick.Controls 2.0

ApplicationWindow {
    id: window
    width: 600
    height: 600
    visible: true

    Slider {
        id: slider
        value: 10
        to: 100
        stepSize: 1
        width: parent.width
        anchors.bottom: parent.bottom
    }

    Flow {
        id: flow
        width: Math.min(implicitW, maxW)
        spacing: 4
        anchors.horizontalCenter: parent.horizontalCenter

        readonly property int columnImplicitWidth: children[0].implicitWidth + spacing
        readonly property int implicitW: Math.max(0, (repeater.count * columnImplicitWidth) - spacing)
        readonly property int maxW: Math.floor(parent.width / columnImplicitWidth) * columnImplicitWidth

        Repeater {
            id: repeater
            model: slider.value
            delegate: Rectangle {
                implicitWidth: 40
                implicitHeight: 60
                color: "transparent"
                border.color: "darkorange"
            }
        }
    }

    Rectangle {
        anchors.fill: flow
        color: "transparent"
        border.color: "red"
    }
}

I think GridLayout is even better for this, though, because unlike Flow, it doesn't leave the extra space on the right edge:

import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0

ApplicationWindow {
    id: window
    width: 600
    height: 600
    visible: true

    Slider {
        id: slider
        value: 10
        to: 100
        stepSize: 1
        width: parent.width
        anchors.bottom: parent.bottom
    }

    GridLayout {
        columns: implicitW < parent.width ? -1 : parent.width / columnImplicitWidth
        rowSpacing: 4
        columnSpacing: 4
        anchors.horizontalCenter: parent.horizontalCenter

        property int columnImplicitWidth: children[0].implicitWidth + columnSpacing
        property int implicitW: repeater.count * columnImplicitWidth

        Repeater {
            id: repeater
            model: slider.value
            delegate: Rectangle {
                implicitWidth: 40
                implicitHeight: 60
                color: "transparent"
                border.color: "darkorange"
            }
        }
    }
}

This is how it looks with GridLayout:

gridlayout-gif

like image 30
Mitch Avatar answered Oct 22 '22 03:10

Mitch


AFAICT the, previous answers don't really answer the question. They only seem to center the flow/grid by itself, but not the elements inside. Items will only be centered as long as there's just 1 row. With multiple rows they will just fill the last row from left to right. So here's an approach which always centers items:

        Flow {
            id: flow
            Layout.fillWidth: true

            property int columns: 3
            property int cellWidth: width / columns
            property int totalRows: flowRepeater.count / columns

            Repeater {
                id: flowRepeater
                model: 5
                delegate: Rectangle {
                    id: flowDelegate
                    width: Math.floor(flow.width / itemsInRow)
                    height: 100
                    color: "transparent"
                    border.width: 1
                    border.color: "red"
                    property int row: Math.floor(index / flow.columns)
                    property int itemsInRow: row < flow.totalRows ? flow.columns : (flowRepeater.count % flow.columns)

                    Rectangle {
                        color: "blue"
                        height: 80
                        width: height
                        radius: width / 2
                        anchors.centerIn: parent
                    }
                }
            }
        }

enter image description here

like image 23
Michael Zanetti Avatar answered Oct 22 '22 05:10

Michael Zanetti