QT QML Gridlayout desperation - qt

I need a fairly simple layout in qml.
I have now struggled for some time with the GridLayout, which I had actually classified as correct for this requirement - but it does not work.
This is how it should be
And this is how it is currently - the right rectangle (yellow) is completely missing.
This is my current code
ApplicationWindow {
property real _encoderWidth: 254
property real _headerHeight: 188
property real _leftSideWidth: 70
property real _keyboard1Height: 100
property real _keyboard2Height: 180
visible: true
id: root
objectName: "mainScreen"
width: 600 + _encoderWidth + _leftSideWidth
height: 800 + _headerHeight + _keyboard1Height + _keyboard2Height
minimumHeight: height
maximumHeight: height
minimumWidth: width
maximumWidth: width
GridLayout {
id: grid
anchors.fill: parent
rows: 4
columns: 3
Rectangle {
id: left
Layout.column: 0
Layout.rowSpan: 4
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: root.height
Layout.maximumHeight: root.height
Layout.minimumWidth: _leftSideWidth
Layout.maximumWidth: _leftSideWidth
color: "blue"
}
Rectangle {
id: top
Layout.row: 0
Layout.column: 1
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: _headerHeight
Layout.maximumHeight: _headerHeight
Layout.minimumWidth: root.width
Layout.maximumWidth: root.width
color: "red"
}
Rectangle {
id: right
Layout.row: 0
Layout.column: 2
Layout.rowSpan: 4
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: root.height
Layout.maximumHeight: root.height
Layout.minimumWidth: _encoderWidth
Layout.maximumWidth: _encoderWidth
color: "yellow"
}
Rectangle {
id: content
Layout.row: 1
Layout.column: 1
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 800
Layout.maximumHeight: 800
Layout.minimumWidth: 600
Layout.maximumWidth: 600
color: "green"
}
Rectangle {
id: bottom1
Layout.row: 2
Layout.column: 1
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: _keyboard1Height
Layout.maximumHeight: _keyboard1Height
Layout.minimumWidth: root.width
Layout.maximumWidth: root.width
color: "lightgray"
}
Rectangle {
id: bottom2
Layout.row: 3
Layout.column: 1
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: _keyboard2Height
Layout.maximumHeight: _keyboard2Height
Layout.minimumWidth: root.width
Layout.maximumWidth: root.width
color: "gray"
}
}
}
The layout does not have to be dynamic, the window size is fixed and cannot be changed.
Any tip or suggestion for improvement is welcome.
Many thanks and best regards
Arne

2 things I found strange in your window:
Why use a layout if you fix all the sizes?
Why use root.width for items in your middle column (except for green, you used 600 there)?It is probably that very point that pushed the yellow rectangle outside the viewport.
I'm not sure these are the proportions you are looking for.
I added 2 variables at the top.
I kept the layout even though sizes are supposed to be fixed.
I set the preferred sizes instead of min/max sizes.Note that since you do not seem to have taken the space between rectangles into consideration (rowSpacing and columnSpacing), the cost of ensuring rectangles fit in the window is that their sizes are going to be slightly different from what you expect.
I removed the explicitly set height for the left and right item (they are supposed to fill all 4 rows they span across.
I removed the explicitly set width for all but 1 item in the middle column (they are all supposed to fill the available width).
This leaves:
ApplicationWindow {
property real _baseWidth: 600
property real _baseHeight: 400
property real _encoderWidth: 254
property real _headerHeight: 188
property real _leftSideWidth: 70
property real _keyboard1Height: 100
property real _keyboard2Height: 180
visible: true
id: root
objectName: "mainScreen"
width: _baseWidth + _encoderWidth + _leftSideWidth
height: _baseHeight + _headerHeight + _keyboard1Height + _keyboard2Height
minimumHeight: height
maximumHeight: height
minimumWidth: width
maximumWidth: width
GridLayout {
id: grid
anchors.fill: parent
rows: 4
columns: 3
Rectangle {
id: left
Layout.row: 0
Layout.column: 0
Layout.rowSpan: 4
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: _leftSideWidth
color: "blue"
}
Rectangle {
id: top
Layout.row: 0
Layout.column: 1
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: _headerHeight
Layout.preferredWidth: _baseWidth
color: "red"
}
Rectangle {
id: content
Layout.row: 1
Layout.column: 1
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: _baseHeight
color: "green"
}
Rectangle {
id: bottom1
Layout.row: 2
Layout.column: 1
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: _keyboard1Height
color: "lightgray"
}
Rectangle {
id: bottom2
Layout.row: 3
Layout.column: 1
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: _keyboard2Height
color: "gray"
}
Rectangle {
id: right
Layout.row: 0
Layout.column: 2
Layout.rowSpan: 4
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: _encoderWidth
color: "yellow"
}
}
}
I let you fine-tune the thing if needed, such as including the spacing of the layout.

Related

How to evenly shrink items in layout to make space for more items

Initially, I have four items with a specific size. Once a 5th item gets added, I would like to shrink the size of all items to make space inside the layout.
Window {
width: 640
height: 480
visible: true
id: root
Item {
id: col
width: 300
height: 200
anchors.centerIn: parent
RowLayout {
id: row
spacing: 10
anchors.centerIn: parent
Rectangle {
Layout.minimumWidth: 30
Layout.preferredWidth: 60
Layout.preferredHeight: 20
Layout.fillWidth: true
color: "red"
}
Rectangle {
Layout.minimumWidth: 30
Layout.preferredWidth: 60
Layout.preferredHeight: 20
color: "blue"
Layout.fillWidth: true
}
Rectangle {
Layout.minimumWidth: 30
Layout.preferredWidth: 60
Layout.preferredHeight: 20
color: "green"
Layout.fillWidth: true
}
Rectangle {
Layout.minimumWidth: 30
Layout.preferredWidth: 60
Layout.preferredHeight: 20
color: "green"
Layout.fillWidth: true
}
Rectangle {
Layout.minimumWidth: 30
Layout.preferredWidth: 60
Layout.preferredHeight: 20
color: "blue"
}
}
}
// for visualization
Rectangle {
width: col.width
height: col.height
x: col.x
y: col.y
color: "black"
opacity: 0.3
}
}
The items should have a size of 60 if there's space to fit all items, or scale down to 30 if there isn't. Any ideas how I would accomplish that?
You were almost there: Set Layout.maximumWidth instead of Layout.preferredWidth and make sure all the elements have Layout.fillWidth set to true. Also, the layout element itself must have the correct size for fillWidth to work. So replace centerIn with fill in this case.
Here is your snippet; corrected and rewritten to make it easier to try out different numbers of elements.
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import QtQuick.Window 2.14
Window {
width: 640
height: 480
visible: true
id: root
Item {
id: col
width: 300
height: 200
anchors.centerIn: parent
RowLayout {
id: row
spacing: 10
anchors.fill: parent
Repeater {
model: spinBox.value
delegate: Rectangle {
Layout.minimumWidth: 30
Layout.maximumWidth: 60
Layout.preferredHeight: 20
Layout.fillWidth: true
color: ["red", "blue", "green", "green", "blue"][index % 5]
}
}
}
}
// for visualization
Rectangle {
width: col.width
height: col.height
x: col.x
y: col.y
color: "black"
opacity: 0.3
}
SpinBox {
id: spinBox
from: 1; to: 10
value: 5
}
}

Get coordinates (screen) of GridLayout items

I have simple QML GridLayout with several QML rectangles layouted in it:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.12
Window
{
width: 640
height: 480
visible: true
title: qsTr("Hello World")
color: "black"
GridLayout
{
id: rootLayout
anchors.fill: parent
anchors.leftMargin: 42
anchors.topMargin: 100
anchors.rightMargin: 56
rows: 4
columns: 3
rowSpacing: 0
columnSpacing: 0
flow: GridLayout.LeftToRight
layoutDirection: Qt.LeftToRight
Rectangle
{
id: redR
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignLeft|Qt.AlignTop
Layout.minimumWidth: parent.width*0.30
Layout.preferredWidth: parent.width*0.30
Layout.maximumWidth: parent.width*0.30
Layout.minimumHeight: 120
Layout.preferredHeight: 120
Layout.maximumHeight: 120
color: "red"
}
Rectangle
{
id: greenR
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignTop
Layout.minimumHeight: 120
Layout.preferredHeight: 120
Layout.maximumHeight: 120
color: "green"
}
Rectangle
{
id: blueR
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight|Qt.AlignTop
Layout.minimumWidth: parent.width*0.30
Layout.preferredWidth: parent.width*0.30
Layout.maximumWidth: parent.width*0.30
Layout.minimumHeight: 120
Layout.preferredHeight: 120
Layout.maximumHeight: 120
color: "blue"
}
Rectangle
{
id: cyanR
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignLeft|Qt.AlignTop
Layout.minimumWidth: 24
Layout.preferredWidth: 24
Layout.maximumWidth: 24
Layout.minimumHeight: 78
Layout.preferredHeight: 78
Layout.maximumHeight: 78
color: "cyan"
}
Rectangle
{
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignTop
Layout.minimumWidth: 32
Layout.preferredWidth: 32
Layout.maximumWidth: 32
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
color: "transparent"
}
Rectangle
{
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight|Qt.AlignTop
Layout.minimumWidth: 24
Layout.preferredWidth: 24
Layout.maximumWidth: 24
Layout.minimumHeight: 78
Layout.preferredHeight: 78
Layout.maximumHeight: 78
color: "transparent"
}
Rectangle
{
id: magentaR
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignLeft|Qt.AlignVCenter
Layout.minimumWidth: 24
Layout.preferredWidth: 24
Layout.maximumWidth: 24
Layout.minimumHeight: 78
Layout.preferredHeight: 78
Layout.maximumHeight: 78
color: "magenta"
}
Rectangle
{
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignVCenter
Layout.columnSpan: 3
color: "transparent"
}
}
}
Now, I am trying to the coordinates of these Rectanangles, however their x and y properties are both 0, even in Component.onCompleted. How do I get these rectangles coordinates, once they are layouted by GridLayout?
qml Rectangle inherits qml Item, so you can use mapToGlobal() function: https://doc.qt.io/qt-5/qml-qtquick-item.html#mapToGlobal-method

QML/QtQuick: make Image occupy only available height in ColumnLayout

I'm developing a mobile app with QML/QtQuick and Qt 5.9.x (Qt 5.10+ is not an option because it doesn't support iOS 8 and 9).
In my vertical layout I'd like to make Image be automatically resized to available height, but I can't figure out how to achieve this: it's always shown in full height. My QML file:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
ApplicationWindow {
id: window
visible: true
// simulate iPhone 6
width: 375
height: 667
ColumnLayout {
anchors.fill: parent
spacing: 0
Text {
text: qsTr("multiline text multiline text multiline text multiline text")
textFormat: Text.PlainText
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
font { weight: Font.Normal; pointSize: 18 }
Layout.fillWidth: true
Layout.topMargin: 20
}
Text {
text: qsTr("title1")
textFormat: Text.PlainText
font { weight: Font.DemiBold; pointSize: 50 }
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30
}
Text {
text: qsTr("title2")
textFormat: Text.PlainText
font { weight: Font.DemiBold; pointSize: 25 }
Layout.alignment: Qt.AlignHCenter
}
Image {
source: "qrc:/Painting.jpg"
verticalAlignment: Image.AlignTop
fillMode: Image.PreserveAspectCrop
// Layout.preferredHeight: 200 // that's how I obtained the second screenshot, but using a constant is not an option of course
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 20
}
Text {
text: qsTr("multiline text multiline text multiline text multiline text")
textFormat: Text.PlainText
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
font { weight: Font.Normal; pointSize: 17 }
Layout.fillWidth: true
Layout.topMargin: 20
}
GridLayout {
Layout.maximumWidth: 300
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 20
Layout.bottomMargin: 30
rows: 3
columns: 3
rowSpacing: 10
columnSpacing: 10
Rectangle {
color: "blue"
Layout.row: 0
Layout.column: 0
Layout.columnSpan: 3
Layout.fillWidth: true
Layout.preferredHeight: 25
}
Rectangle {
color: "blue"
Layout.row: 1
Layout.column: 0
Layout.fillWidth: true
Layout.preferredHeight: 25
}
Rectangle {
color: "blue"
Layout.row: 1
Layout.column: 1
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.preferredHeight: 25
}
Rectangle {
color: "blue"
Layout.row: 2
Layout.column: 0
Layout.fillWidth: true
Layout.preferredHeight: 25
}
Rectangle {
color: "blue"
Layout.row: 2
Layout.column: 1
Layout.fillWidth: true
Layout.preferredHeight: 25
}
Rectangle {
color: "blue"
Layout.row: 2
Layout.column: 2
Layout.fillWidth: true
Layout.preferredHeight: 25
}
}
}
}
First image is how it's shown now, second one is how I want it to be: (screenshots are from desktop, but on mobile the result is the same)
I know how to achieve the desired behavior on iOS through AutoLayout (play with image's hugging priority and/or compression resistance), but I can't find anything similar in QML/QtQuick.
Using Layout.fillHeight will automatically resize the Image to the available height:
Image {
// ...
fillMode: Image.PreserveAspectCrop
Layout.fillHeight: true
}

Items not correctly spread across GridLayout

I have the following custom QML Item, which will represent a pin code entry GUI element:
import QtQuick 2.0
import QtQuick.Layouts 1.2
import QtQuick.Controls 1.3
import "../items"
Item
{
id: ueKeypad
width: 512
height: 512
Rectangle
{
id: ueKeypadWrapper
antialiasing: true
anchors.fill: parent
ColumnLayout
{
id: ueKeypadLayoutMain
antialiasing: true
layoutDirection: Qt.LeftToRight
spacing: 8
anchors.fill: parent
ColumnLayout
{
id: ueKeypadTitleLayout
layoutDirection: Qt.LeftToRight
Layout.fillWidth: true
Layout.minimumHeight: 24
Layout.preferredHeight: 24
Layout.maximumHeight: 24
Text
{
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("PIN ENTRY")
clip: true
font.bold: true
font.pointSize: 24
textFormat: Text.RichText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
} // Text
} // ColumnLayout
GridLayout
{
id: ueKeypadNumbersLayout
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignVCenter
layoutDirection: Qt.LeftToRight
columnSpacing: 8
rowSpacing: 8
flow: GridLayout.LeftToRight
columns: 3
UeButton
{
id: ueKeypadButton1
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
ueText: qsTr("1")
}
UeButton
{
id: ueKeypadButton2
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
ueText: qsTr("2")
}
UeButton
{
id: ueKeypadButton3
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
ueText: qsTr("3")
}
UeButton
{
id: ueKeypadButton4
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
ueText: qsTr("4")
}
UeButton
{
id: ueKeypadButton5
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
ueText: qsTr("5")
}
UeButton
{
id: ueKeypadButton6
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
ueText: qsTr("6")
}
UeButton
{
id: ueKeypadButton7
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
ueText: qsTr("7")
}
UeButton
{
id: ueKeypadButton8
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
ueText: qsTr("8")
}
UeButton
{
id: ueKeypadButton9
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
ueText: qsTr("9")
}
} // GridLayout
RowLayout
{
id: ueKeypadActionLayout
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignVCenter
layoutDirection: Qt.LeftToRight
spacing: 8
UeButton
{
id: ueKeypadButtonOk
ueText: qsTr("Ok")
} // UeButton
UeButton
{
id: ueKeypadButton0
ueText: qsTr("0")
Layout.minimumHeight: 32
Layout.preferredHeight: 32
Layout.maximumHeight: 32
} // UeButton
UeButton
{
id: ueKeypadButtonCancel
ueText: qsTr("Cancel")
} // UeButton
} // RowLayout
} // ColumnLayout
} // Rectangle
} // Item
It uses custom QML Button, named UeButton:
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick 2.5
Button
{
property string ueText
id: ueButton
text: ueText
style: ButtonStyle
{
background: Rectangle
{
antialiasing: true
smooth: true
gradient: Gradient
{
GradientStop
{
position: 0
color: "#ffffff"
} // GradientStop
GradientStop
{
position: 0.418
color: "#000000"
} // GradientStop
} // Gradient
border.color: "steelblue"
border.width: control.activeFocus?2:1
radius: 4
} // background
label: Text
{
color: "#ffffff"
font.bold: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pointSize: 16
text: control.text
} // label
} // ButtonStyle
} // ueButton
If I view first code in QtCreator's Designer Tool, I get the following situation:
Why are Buttons not spread across GridLayout? Same goes to lower RowLayout, whose Items (three Items of type UeButton) are not centered and aligned across whole RowLayout?
The attached properties of a Layout ensures that the Items contained are correctly sized w.r.t. the given constraints.
Items can be stretched to fill the available space (fillWidth/fillHeight), forced to not shrink under a certain value (minimumWidth/minimumHeight) or not enlarge over a certain other value (maximumWidth/maximumHeight). You can also force an Item to occupy more than one row/column (rowSpan/columnSpan) and to assume a specific size (preferredWidth/preferredHeight which implies minimum == maximum).
The precedence is:
preferred <minimum/maximum < width/height
Setting a property to the left automatically discard any to the right. You can easily understand the reasoning behind this. This scheme can be somewhat "broken" by implicitWidth/implicitHeight since the size of any Item cannot shrink under these values. fill properties ensure that the Item fills the available space, according to the constrains above: if fill is not defined an Item cannot grow or shrink according to its constrains.
Now, if you want the Buttons to maintain they aspect and still stretch the grid, you can use an intermediate Item. While applying Layout attached properties to the external Item, the Buttons can be centerIn it and be unaffected.
Sample code:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Window {
width: 200; height: 200; minimumHeight: 100; visible: true
GridLayout {
anchors.fill: parent
rows: 3
columns: 3
Repeater {
model: 9
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Button { anchors.centerIn: parent; width: 32; height: 32; text: index + 1 }
}
}
}
}
Instead if you want the Buttons to fill the available space, just specify fillWidth/fillHeight. Since no other layout constraints are set (e.g. minimum*, maximum*) the Buttons correctly occupy all the available space. Here is the code above revisited. As expected, width and height are simply discarded:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Window {
width: 200; height: 200; minimumHeight: 100; visible: true
GridLayout {
anchors.fill: parent
rows: 3
columns: 3
Repeater {
model: 9
Button {width: 32; height: 32; text: index + 1; // 32? NOPE!
Layout.fillWidth: true; Layout.fillHeight: true
}
}
}
}

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:
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:
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?
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?
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:
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.
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".
While both other answers show valid solutions, I believe both the question being asked and the two solutions somehow miss the point of using Layouts.
Basically, Layouts are made to bring together Items that have an implicit size (implicitHeight/implicitWidth). Layout.preferredWidth/Layout.preferredHeight are used to override these things in some rare situations, see below. The "Qt Quick Layouts - Basic Example" coming with Qt does not use Layout.preferredWidth/Layout.preferredHeight at all (!) and makes a really nice look, without contaminating the whole qml file with either anchors or Layout properties. It takes some learning to be able to do this oneself, but once you got used to it, Layouts are a way to define user interfaces more directly with less code.
What confused me the most at the beginning were the following things:
RowLayout/ColumnLayout/GridLayout come with Layout.fillWidth/Layout.fillHeight set to true, so when putting these near an Item/Rectangle then the Items/Rectangles suddenly disappear, because they don't have set these values (i.e. they have Layout.fillWidth/Layout.fillHeight set to false).
Items/Rectangles come with an implicitHeight/implicitWidth of 0, meaning they don't really play nice side-by-side with Layouts. The best thing to do is to derive implicitWidth/implicitHeight from contained subitems, like a RowLayout/ColumnLayout itself does by default for its subitems.
Layout.preferredWidth/Layout.preferredHeight can be used to overcome implicit sizes where they are already defined and cannot be set. One such place is directly in a layout item, another is e.g. a Text item which also doesn't let you override implicit sizes.
Considering these points, I would write the example in the following way. I removed unnecessary items to better illustrate when Layout.fillwidth/Layout.fillheight are needed, and when it is better to use implicitWidth in my opinion.
import QtQuick 2.9
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
width: 250
height: 150
visible: true
ColumnLayout {
spacing: 0
anchors.fill: parent
Rectangle {
implicitHeight: 40
Layout.fillHeight: true
Layout.fillWidth: true
color: "red"
}
RowLayout {
spacing: 0
Layout.preferredHeight: 20
Rectangle {
implicitWidth: 20
Layout.fillHeight: true
Layout.fillWidth: true
color: "darkGreen"
}
Rectangle {
implicitWidth: 80
Layout.fillHeight: true
Layout.fillWidth: true
color: "lightGreen"
}
}
RowLayout {
spacing: 0
Layout.preferredHeight: 40
Rectangle {
implicitWidth: 40
Layout.fillHeight: true
Layout.fillWidth: true
color: "darkBlue"
}
Rectangle {
implicitWidth: 20
Layout.fillHeight: true
Layout.fillWidth: true
color: "blue"
}
Rectangle {
implicitWidth: 40
Layout.fillHeight: true
Layout.fillWidth: true
color: "lightBlue"
}
}
}
}
It is forbidden (and unnecessary) to try and reference width and height of the parent from Items inside the Layout.
When fillWidth (or fillHeight) is set to true, then Items are allocated space in proportion to their specified preferredWidth (or preferredHeight).
Therefore the correct way to create your Layout is as follows. I have modified the appearance only to show that spacing and Text can also be set freely as desired. No binding loops.
ApplicationWindow {
x: 500
y: 100
width: 250
height: 150
visible: true
ColumnLayout {
anchors.fill: parent
spacing: 5
RowLayout {
spacing: 5
Layout.preferredHeight: 40
Layout.fillHeight: true
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
color: "red"
}
}
RowLayout {
spacing: 5
Layout.preferredHeight: 20
Layout.fillHeight: true
Rectangle {
Layout.fillHeight: true
Layout.preferredWidth: 20
Layout.fillWidth: true
color: "darkGreen"
}
Rectangle {
Layout.fillHeight: true
Layout.preferredWidth: 80
Layout.fillWidth: true
color: "lightGreen"
}
}
RowLayout {
spacing: 5
Layout.preferredHeight: 40
Layout.fillHeight: true
Text {
Layout.fillHeight: true
Layout.preferredWidth: 40
Layout.fillWidth: true
color: "darkBlue"
text: "hello world!"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
Rectangle {
Layout.fillHeight: true
Layout.preferredWidth: 20
Layout.fillWidth: true
color: "blue"
}
Rectangle {
Layout.fillHeight: true
Layout.preferredWidth: 40
Layout.fillWidth: true
color: "lightBlue"
}
}
}
}
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.

Resources