Search code examples
qtqmlqtquickcontrols2

Why is loop created in this case?


This examples gives me property binding errors:

file:///home/user/qmltests/layouts.qml:22:4: QML Label: Binding loop detected for property "font.pixelSize"
file:///home/user/qmltests/layouts.qml:22:4: QML Label: Binding loop detected for property "font.pixelSize"
file:///home/user/qmltests/layouts.qml:18:4: QML Label: Binding loop detected for property "font.pixelSize"

Code:

import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11

Page {
    id: root
    width: 400
    height: 200
    StackLayout {
        id: main_container
        Layout.fillWidth:true
        Layout.fillHeight:true
        ColumnLayout {
            id: sub_container
            Layout.fillWidth:true
            Layout.fillHeight:true
            Label {
                text: "One"
                font.pixelSize: sub_container.height*0.2
            }
            Label {
                text: "Two"
                font.pixelSize: sub_container.height*0.2
            }
        }
    }
}

By logic, this shouldn't happen, because I am copying the width and height down to lower level components by using Layout.fillWidth=true and layout.fillHeight=true

To fix this error, I have to copy the heigth from the root element:

import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11

Page {
    id: root
    width: 400
    height: 200
    StackLayout {
        id: main_container
        Layout.fillWidth:true
        Layout.fillHeight:true
        ColumnLayout {
            id: sub_container
            Layout.fillWidth:true
            Layout.fillHeight:true
            Label {
                text: "One"
                font.pixelSize: root.height*0.2
            }
            Label {
                text: "Two"
                font.pixelSize: root.height*0.2
            }
        }
    }
}

Why aren't width and height propagated from the root elements down to children layouts?

How can I reference sub_container.width and sub_container.height (because it is known before items are laid out) without getting binding loop error? I don't want to reference the root item because due to complexity there may be many layouts inside root item and in order to lay out components in a scalable way I need to know the width and height of the parent layout.


Solution

  • If you use layouts, the elements they manage must not change their size based on size given by the layout. To do what you wish to do, you shouldn’t be using a layout, but anchors, since you want to manage the child sizes manually. The loop is there because the layout uses the size of your item to resize itself, that your item then uses to resize itself, endlessly. If you don’t need that functionality, it will interfere – as you have seen. The reason it worked via root is that root’s size is not managed by the layout: it’s fixed. And that’s what you wanted all along, isn’t it?

    Another approach would be for the label not to change its size hint based on font size, so that the layout wouldn’t react to the font size change.

    TL;DR: Layouts size themselves based on child sizes, thus there’s a loop if the child sizes itself based on the layout’s size.