I want to create a 3x2 Grid and put 6 elements inside of it. I want these 6 elements to have automatic size, that is, have the width and height of one square of the grid.
GridLayout {
columns: 3
rows: 2
width: 1280
height: 720
MyCustomCppItem {
someProperty: "hello"
}
}
but if I don't specify a width and height my item isn't even instantiated. How should I do?
If you don't want to specify a size, you have to set the properties Layout.fillHeight: true and Layout.fillWidth: true in each item in your layout.
The layout will arrange the items to occupy all the available space.
Related
I have a ListView (horizontal orientation) in my qml containing some fixed-size elements. I want items to be spaced out to fill the entiew width of ListView element. So if there are less elements I want them to be spaced out more. Basically what I need is exactly like Layout.fillWidth = true property of RowLayout but for ListView.
I can count how many items I have, then subtract total items width from ListView width, divide by items count and assign the spacing but it seems too silly to do.
Is there a way to do this automatically like in RowLayout?
Or maybe I need to use something different from ListView for this? Something like RowLayout but that I can assign my list data model to?
You can accomplish what you want with a ListView, you just need to adjust the spacing dynamically based on how many delegates you have. This example will break down if your delegates are differently-sized (as this is based only on the width of the first delegate), or if the delegates cumulatively exceed the width of the ListView.
ListView {
width: 500
orientation: Qt.Horizontal
model: 6
spacing: {
if (count > 0) {
return (width - (itemAtIndex(0).width * count))/(count - 1)
} else {
return 0
}
}
delegate: Rectangle {
implicitHeight: 50
implicitWidth: 50
color: "red"
border.width: 1
}
}
ListView may not be the most appropriate container for this task. I say this because it has a built in ScrollView and other behaviors that it sounds like you don't need. If all you need is a simple row of a few identically-sized delegates, I agree with scopchanov and believe that a Repeater inside a RowLayout would be the best option. Here is a simple example:
RowLayout {
width: 500
Repeater {
model: 6
delegate: Rectangle {
implicitHeight: 50
implicitWidth: 50
color: "tomato"
border.width: 1
Layout.alignment: Qt.AlignHCenter // must be set to align the rectangles within their empty space
}
}
}
You may notice that this introduces gaps to the left and right, if these gaps are unacceptable, you may need to set the spacing on the RowLayout in the same manner as the ListView example instead.
I want to create a breadcrumb in QML.
So to have a dynamic C++ model, displayed as an horizontal list of items. Each item has a fixed size to only display its content (basically a string).
I need to display all items (with the maximum of almost my window width), on top of my main background (a working area), so transparent except for the items.
What is the best container to use for that case ? Considering I do not need to scroll.
Row, RowLayout, ListView ?
Here is a code for my comment above. Flow could be replaced with Row if needed.
Flow {
spacing: 3
width: parent.width
Repeater {
model: ["root", "item1","subitem1","one more items","subitem2","item3"]
delegate: Label {
padding: 10
text: modelData
background: Rectangle {
color: "#DEDEDE"
}
}
}
}
In following listview's items, length of text can be different (10, or 1000 characters), so I want make each list view item height fit text height.
(Like css height: auto).
Component {
id: sysNotificationsDelegate
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: childrenRect.height
color: "#eee"
Text {
text: model.body // <-- TEXT SIZE CAN BE DIFFERENT
wrapMode: Text.WordWrap
}
}
}
ListView {
anchors.fill: parent
spacing: 10
model: ListModel {
id: listModel
}
delegate: sysNotificationsDelegate
}
What is proper and most performant way to achieve this? (taking into account that I will have a lot of such elements and I've read that property bindings in qml have some additional performance cost)
(Qt 5.10)
For a Text to be able to wrap (or elide), it needs to have a width set, otherwise it will expand with no limit.
Text has 3 important width properties:
width: consider it the max width of a line of your text, it should always be set unless you don't want to limit the width of your screen (you can scroll or pan horizontally). Setting wrapMode or elide with no width will have no effect.
implicitWidth: the width the text would need to occupy to fit in a single line. Includes the left and right padding. Does not depend on the explicit width. Not sure when to use it ¯\_(ツ)_/¯ any ideas in the comments?
contentWidth: the width the text is actually occupying taking in account the wrapping and eliding. Does not include the left and right padding. Depends on the explicit width. Use this to query the width of your text, like when you want to draw a box around some text (a chat bubble for example).
The same corresponding properties exist for height too. maximumLineCount can limit the height of a text in addition to an explicit height
That means that in your case you want to do:
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: text.contentHeight // contentHeight, height and implicitHeight are all the same here since there's no padding, no maximumLineCount and height isn't explicitely set
color: "#eee"
Text {
id: text
text: model.body // <-- TEXT SIZE CAN BE DIFFERENT
width: parent.width // remember, width = max width for Text
wrapMode: Text.WordWrap
}
}
What is the difference between width/height and implicitWidth/Height in QML? When should one set the implicit dimensions instead of the regular? When should one ask the implicit dimensions instead of the regular from a component/item?
Generally, usage of implicitHeight/Width only makes sense within reusable components.
It gives a hint, of the natural size of the Item without enforcing this size.
Let's take a Image as an example. The natural size of the image would map one pixel from the image file to one pixel on the screen. But it allows us to stretch it, so the size is not enforced and can be overridden.
Let's say now, we want to have a gallery with pictures of unknown dimension, and we don't want to grow but only shrink them if necessary. So we need to store the natural size of the image. That is, where the implicit height comes into play.
Image {
width: Math.max(150, implicitWidth)
height: Math.max(150, implicitHeight)
}
In custom components, you have a choice on how to define the sizes.
The one choice is, to have all dimensions relative to the components root-node, maybe like this:
Item {
id: root
Rectangle {
width: root.width * 0.2
height: root.height * 0.2
color: 'red'
}
Rectangle {
x: 0.2 * root.width
y: 0.2 * root.height
width: root.width * 0.8
height: root.height * 0.8
color: 'green'
}
}
In this case, there is no natural size of the object. Everything works out perfectly for each size you set for the component.
On the other hand, you might have an object, that has a natural size - that happens, e.g. if you have absolute values in it
Item {
id: root
property alias model: repeater.model
Repeater {
id: repeater
delegate: Rectangle {
width: 100
height: 100
x: 102 * index
y: 102 * index
}
}
}
In this example you should provide the user with information about the natural size, where the content does not protude the item. The user might still decide to set a smaller size and deal with the protrusion, e.g. by clipping it, but he needs the information about the natural size to make his decision.
In many cases, childrenRect.height/width is a good measure for the implcitHeight/Width, but there are examples, where this is not a good idea. - e.g. when the content of the item has x: -500.
A real life example is the Flickable that is specifically designed to contain larger objects than its own size. Having the size of the Flickable to be equal to the content would not be natural.
Also be careful, when using scale in custom components, as the childrenRect will not know about the scaling.
Item {
id: root
implicitWidth: child.width * child.scale
implicitHeight: child.height * child.scale
Rectangle {
id: child
width: 100
height: 100
scale: 3
color: 'red'
}
}
And to your comment: I just don't understand why it is better to set implicitWidth/Height instead of setting width/height of a component's root dimension.
implicitWidht/Height are not a necessety - QtQuick could do without them. They exist for convenience and shall be convention.
Rule of Thumb
When you want to set dimension of a root node of a reusable component, set implicitWidth/Height.
In some cases, set it for non-root-nodes, if the nodes are exposed as a property.
Do so only, if you have a reason for it (many official components come without any).
When you use a component, set width/height.
I don't have the definitive answer but I can tell you what I found out. First, from the documentation:
implicitWidth : real
Defines the natural width or height of the Item if no width or height
is specified.
The default implicit size for most items is 0x0, however some items
have an inherent implicit size which cannot be overridden, for
example, Image and Text.
but less informative for width:
width
Defines the item's position and size.
The width and height reflect the actual size of the item in the scene. The implicit size is some kind of inherent property of the item itself.1
I use them as follows: When I create a new item and it can be resized, I set an implicit size inside the object2. When I'm using the object, I often set the real size explicitly from the outside.
The implicit size of an object can be overridden by setting height and width.
an example: TextWithBackground.qml
Item {
implicitWidth: text.implicitWidth
implicitHeight: text.implicitHeight
// the rectangle will expand to the real size of the item
Rectangle { anchors.fill: parent; color: "yellow" }
Text { id: text; text: "Lorem ipsum dolor..." }
}
an example: MyWindow.qml
Item {
width: 400
height: 300
TextWithBackground {
// half of the scene, but never smaller than its implicitWidth
width: Math.max(parent.width / 2, implicitWidth)
// the height of the element is equal to implicitHeight
// because height is not explicitly set
}
}
1) For some elements, like Text, the implicit height depends on the (not-implicit) width.
2) The implicit size usually depends on the implicit size of its children.
Implicit size is supposed to be used when calculating size of an item based on its contents. Whereas setting width or height on a parent item may affect the size of its children it should never be a case, when you set implicit size.
Rule of thumb
Implicit size should only "bubble up", i.e. children should never
lookup for implicit size of their parent to calculate their own
implicit size, neither parent should try to force implicit size of its
children.
If you would try to set width on a component similar to layout, that initially calculates its width from the width (rather than implicitWidth) of its child item and that child is affected by the size of a parent, you would end up with a binding loop.
This is why the property exists - to break cyclic dependencies when calculating size of an item based on its contents.
I am adding Image objects dynamically to a GridLayout in a QML file in qt. I want to position the image items so that each new image item comes to the right side of the previous one and if there is no place in that row, it is placed directly to the next row. It is exactly the way how I typed this question, i.e. letters are my image items and text box is the layout.
However the output is following:
The layout leaves space between items as seen in the screenshot. I've read through the properties of gridlayout and tried many combinations out but didn't come up with the right combination. These are the properties I set so far:
GridLayout {
id: myLayout
anchors.fill: parent
columnSpacing: preferredColumnSpacing
rowSpacing: preferredRowSpacing
Component {
id: myImageComponent
Image {
visible: true
Layout.alignment: (Qt.AlignLeft | Qt.AlignTop)
Layout.preferredWidth: preferredImageWidth
Layout.preferredHeight: preferredImageHeight
}
}
}
Which modification gives me the expected behaviour?
Edit: in the screenshot above, the layout has 9 columns, 4 rows but 17 image items.