0
votes

I need some elements which are pixel-perfect, and others which should be resizable, so I try to avoid layouts, using only anchors.

If I have an item with a fixed size as the "main canvas", I can do anything necessary for my goals with anchoring. However, it doesn't work with the main window. If I have an item which is anchored to the main window, its size is reported as zero

ApplicationWindow
{
    id: mainWindow
    width: 1024
    height: 768
    minimumWidth: 800
    minimumHeight: 480
    Component.onCompleted: { console.log("mainWindow width: " + width) }

    Item
    {
        id: topLevelItem
        anchors.fill: parent
        Component.onCompleted: { console.log("topLevelItem width: " + width) }
    }
}

Interestingly, the width of topLevelItem is printed as 0, while the width of mainWindow is correctly printed as 1024.

I added a few elements, and now it gets weird:

ApplicationWindow
{
    id: mainWindow
    width: 1024
    height: 768
    minimumWidth: 800
    minimumHeight: 480
    Component.onCompleted: { console.log("mainWindow width: " + width) }

    Item
    {
        id: topLevelItem
        anchors.fill: parent
        Component.onCompleted: { console.log("topLevelItem width: " + width) }

        Rectangle
        {
            id: red
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.left: parent.left

            width: 100
            color: "#FF0000"

            Component.onCompleted: { console.log("red width: " + width) }
        }


        Rectangle
        {
            id: green

            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.left: red.right
            anchors.right: blue.left

            color: "#00FF00"

            Component.onCompleted: { console.log("green width: " + width) }
        }

        Rectangle
        {
            id: blue

            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.right: parent.right

            width: 50
            color: "#0000FF"

            Component.onCompleted: { console.log("blue width: " + width) }
        }

    }

}

Everything is displayed as it should be. There is a 100 px vertical red bar at the left, an 50 px vertical blue bar at the right edge, and the rest is filled with green.

What is surprising, is that the sizes are wrong:

qml: topLevelItem width: 0
qml: blue width: 50
qml: green width: -150
qml: red width: 100
qml: mainWindow width: 1024

The height of everything, except for mainWindow, is reported as zero.

I need access to the sizes, especially to the size of the middle area, because it has to influence what kind of other elements will be placed, depending on the available space.

What can be the cause of this strange behavior, and did I do something wrong? How can I correctly access the size of the middle rectangle?

1

1 Answers

3
votes

QML is a declarative language, which means you can find yourself fighting against it if you try to rely too much on the order of evaluation of certain things. If you print out the width of topLevelItem as it changes, you'll see that it is eventually correct:

import QtQuick 2.7
import QtQuick.Controls 1.0

ApplicationWindow {
    id: mainWindow
    width: 1024
    height: 768
    minimumWidth: 800
    minimumHeight: 480
    visible: true

    Component.onCompleted: console.log("mainWindow width: " + width)

    Item {
        id: topLevelItem
        anchors.fill: parent
        onWidthChanged: print("topLevelItem width: " + width)
        Component.onCompleted: console.log("topLevelItem width: " + width)
    }
}

Output:

qml: topLevelItem width: 0
qml: mainWindow width: 1024
qml: topLevelItem width: 1024

I'm not sure what happens exactly, but it's likely that the window gets its size first and then tells the contentItem that it can begin laying out its items and giving them sizes. This happens asynchronously and is one of the reasons why imperative code shouldn't be relied upon in some cases.

It's also worth pointing out that you can have both pixel-perfect items and resizable ones when using layouts; use e.g. Layout.minimumWidth and Layout.maximumWidth to force certain sizes.