Search code examples
qtlayoutqmlfluid-layout

How to design a multi-level fluid layout in QML


I have designed a layout in QML to learn more about its features and have some questions on the "Best Practices" in designing such layout. Here it is:

enter image description here

It is essentially a ColumnLayout consisted of three RowLayouts, each one with some Rectangles. The size of each Row and Rectangle should be calculate such as:

  • First row: Height = 40%, Width = 100%
    • Red Rectangle filling the whole area
  • Second row: Height = 20%, Width = 100%
    • Dark-green Rectangle: Height = 100%, Width = 20%,
    • Light-green Rectangle: Height = 100%, Width = 80%
  • Third row: Height = 40%, Width = 100%
    • Dark-blue Rectangle: Height = 100%, Width = 40%,
    • Blue Rectangle: Height = 100%, Width = 20%
    • Light-blue Rectangle: Height = 100%, Width = 40%

The QML I have came up with is working and is in the following. I have some questions about it:

  1. I have set the width and height percentages using Layout.preferredHeight: x*parent.height pattern. Other options caused some issues (e.g. preferredHeight caused binding loop warnings). Is my approach correct and efficient?
  2. As a hack, I set Layout.fillWidth: true for the first element of Row #2 and Row #3, which doesn't make sense to me, but does work. If I set their width as percentage (e.g. Layout.preferredWidth: 0.2*parent.width) their row will collapse to width 0. Is this an expected behavior? Is there any better workaround?
  3. Do you have any recommendation on the layouts? Am I on the right path?

Here is my QML code for the layout:

ApplicationWindow {
    x: 500
    y: 100
    width: 250
    height: 150
    visible: true

    ColumnLayout {
        anchors.fill: parent
        spacing: 0
        RowLayout {
            spacing: 0
            Layout.preferredHeight: 0.4*parent.height
            Layout.fillHeight: false
            Rectangle {
                Layout.fillHeight: true
                Layout.fillWidth: true
                color: "red"
            }
        }
        RowLayout {
            spacing: 0
            Layout.preferredHeight: 0.2*parent.height
            Layout.fillHeight: false
            Rectangle {
                Layout.fillHeight: true
                Layout.fillWidth: true
                color: "darkGreen"
            }
            Rectangle {
                Layout.fillHeight: true
                Layout.preferredWidth: 0.8*parent.width
                color: "lightGreen"
            }
        }
        RowLayout {
            spacing: 0
            Layout.preferredHeight: 0.4*parent.height
            Layout.fillHeight: false
            Rectangle {
                Layout.fillHeight: true
                Layout.fillWidth: true
                color: "darkBlue"
            }
            Rectangle {
                Layout.fillHeight: true
                Layout.preferredWidth: 0.2*parent.width
                color: "blue"
            }
            Rectangle {
                Layout.fillHeight: true
                Layout.preferredWidth: 0.4*parent.width
                color: "lightBlue"
            }
        }
    }
}

Update:

My approach seems to be more hacky than I expected:

  1. Putting Text elements as children in this layout raises binding loop warnings like:

QML QQuickLayoutAttached: Binding loop detected for property "preferredWidth"

If a wrap Text inside a Rectangle the warnings disappear.

  1. The spacing: 0 seems to play an important role. Omitting it will causes binding loop warnings.

While my approach to fluid layout design in QML works, it has some serious issue and might not fall under the "best practices".


Solution

  • QtQuick.Layout does not provide any real improvements over the classical anchoring system. I would recommand to avoid them. You can have way more control over your layout using anchors.

    Here is the exact same design without QtQuick.Layout :

    ApplicationWindow {
        x: 500
        y: 100
        width: 250
        height: 150
        visible: true
    
        Column {
            anchors.fill: parent
    
            Row {
                anchors.left: parent.left
                anchors.right: parent.right
                height: 0.4 * parent.height
    
                Rectangle {
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    width: parent.width
                    color: "red"
                }
            }
    
            Row {
                anchors.left: parent.left
                anchors.right: parent.right
                height: 0.2 * parent.height
    
                Rectangle {
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    width: 0.2 * parent.width
                    color: "darkGreen"
                }
    
                Rectangle {
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    width: 0.8 * parent.width
                    color: "lightGreen"
                }
            }
    
            Row {
                anchors.left: parent.left
                anchors.right: parent.right
                height: 0.4 * parent.height
    
                Rectangle {
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    width: 0.4 * parent.width
                    color: "darkBlue"
                }
                Rectangle {
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    width: 0.2 * parent.width
                    color: "blue"
                }
                Rectangle {
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    width: 0.4 * parent.width
                    color: "lightBlue"
                }
            }
        }
    }
    

    So far I never met any design that was impossible to do without QtQuick.Layout.