Search code examples
layoutqmlqt-quickqtquickcontrolsqtquickcontrols2

QML / Qt Quick weird layout


I am using QML (Qt Quick) in order to set-up the GUI of an app and I am having weird behaviors.

Here is my initial piece of code:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

Rectangle {
    id: rectangle1
    color: palette.grey

    ColumnLayout
    {
        id: columnLayout1

        Label
        {
            id: label1
            text: qsTr("Target")
            font.pointSize: 22
        }

        ProgressBar
        {
            id: progressBar1
            value: 0.5
        }
    } }

... which gives layout:

Initial layout

Then I anchor the centers:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

Rectangle
{
    id: rectangle1
    color: palette.grey

    ColumnLayout
    {
        id: columnLayout1
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter

        Label
        {
            id: label1
            text: qsTr("Target")
            font.pointSize: 22
        }

        ProgressBar
        {
            id: progressBar1
            value: 0.5
        }
    }
}

No problem here, I get:

enter image description here

Now things go wrong when I rotate the slider:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

Rectangle
{
    id: rectangle1
    color: palette.grey

    ColumnLayout
    {
        id: columnLayout1
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter

        Label
        {
            id: label1
            text: qsTr("Target")
            font.pointSize: 22
        }

        ProgressBar
        {
            id: progressBar1
            rotation: 90
            value: 0.5
        }
    }
}

And here I was expecting Qt to rotate the slider and use the rotated slider to compute the layout, but it is not what happens. Actually the layout seems to be computed with the slider unrotated then only the slider is rotated. This ends up with a layout which is no longer a column:

enter image description here

This looks a bit buggy, no? Or am I doing something wrong?

Now another problem occurs when I wrap the slider in an Item (I was actually playing with Items to see if I could find a workaround):

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

Rectangle
{
    id: rectangle1
    color: palette.grey

    ColumnLayout
    {
        id: columnLayout1
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter

        Label
        {
            id: label1
            text: qsTr("Target")
            font.pointSize: 22
        }

        Item
        {
            ProgressBar
            {
                id: progressBar1
                value: 0.5
            }
        }
    }
}

And in this case what I get is a slider above the label (although it is defined after it in the code):

enter image description here

This also looks weird to me... Anybody has an idea of what I did wrong?

Please note that the screen shots are captured from Designer but running the program leads to the exact same problems.

Kind regards,

Antoine Rennuit.


Solution

  • Now things go wrong when I rotate the slider

    Rotation doesn't affect the width or height properties. You can check this by adding the following lines to your ProgressBar:

    onWidthChanged: print("width", width)
    onHeightChanged: print("height", height)
    

    If ProgressBar had an orientation property, like it does in Qt Quick Controls 1, you could use that instead of rotation. You can't simply flip the width and height when rotating the bar, either, because layouts use the implicitWidth and implicitHeight, not width and height:

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.3
    
    import QtQuick.Controls 2.1
    
    ApplicationWindow {
        width: 400
        height: 300
        visible: true
    
        ColumnLayout {
            id: columnLayout1
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
    
            Label {
                id: label1
                text: qsTr("Target")
                font.pointSize: 22
            }
    
            ProgressBar {
                id: progressBar1
                rotation: 90
                width: implicitHeight
                height: implicitWidth
                value: 0.5
            }
        }
    }
    

    You could calculate the implicitWidth and implicitHeight yourself, but that's not nice.

    Now another problem occurs when I wrap the slider in an Item

    You're on the right track here. As mentioned in this answer, you need to give the Item a size. You can do so like this:

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.3
    
    import QtQuick.Controls 2.1
    
    ApplicationWindow {
        width: 400
        height: 300
        visible: true
    
        ColumnLayout {
            id: columnLayout1
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
    
            Label {
                id: label1
                text: qsTr("Target")
                font.pointSize: 22
            }
    
            Item {
                implicitWidth: progressBar1.implicitHeight
                implicitHeight: progressBar1.implicitWidth
    
                ProgressBar {
                    id: progressBar1
                    rotation: 90
                    value: 0.5
                }
            }
        }
    }
    

    Notice that the progress bar still isn't in the correct position. That's because the default transform origin for items is in the centre. Setting it to the BottomLeft works:

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.3
    
    import QtQuick.Controls 2.1
    
    ApplicationWindow {
        width: 400
        height: 300
        visible: true
    
        ColumnLayout {
            id: columnLayout1
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
    
            Label {
                id: label1
                text: qsTr("Target")
                font.pointSize: 22
            }
    
            Item {
                implicitWidth: progressBar1.implicitHeight
                implicitHeight: progressBar1.implicitWidth + progressBar1.implicitHeight
    
                ProgressBar {
                    id: progressBar1
                    rotation: 90
                    transformOrigin: Item.BottomLeft
                    value: 0.5
                }
            }
        }
    }
    

    Note that you also have to add the implicitHeight of the bar to the implicitHeight of the Item, in order to account for the transform origin change we did.

    I'd strongly recommend you create a suggestion for an orientation property on bugreports.qt.io. :)