I'm trying to size a Rectangle to the size of its content:
ListView {
anchors.fill: parent
model: ["a", "b", "c"]
delegate: Rectangle {
color: "gray"
property int margin: 20
width: childrenRect.width + 2 * margin
height: childrenRect.height + 2 * margin
Text {
text: modelData
anchors.centerIn: parent
}
}
}
It seems to work visually, but the QML engine gives a lot of these errors:
qrc:/main.qml:13:19: QML Rectangle: Binding loop detected for property "width"
qrc:/main.qml:13:19: QML Rectangle: Binding loop detected for property "height"
Where is the binding loop and how to fix it?
The error comes from the item Rectangle: its size is based on its content (by using childrenRect). But, your Text is placed according to the size of its parent (by using centerIn).
The position of Textis based on the size of its parent (centered in the Rectangle). childrenRect holds the collective geometry of the children (such as bounding rect property). When Rectangle is resized, the position of Text changes also. Then, the childrenRect will change too to map the new position of Text.
So, you will create a loop if you map the size of Rectangle on the children rect property and you center Text in Rectangle.
A quick example to add a background to a text centered in a list view:
ListView {
id: list
anchors.fill: parent
model: ["a", "b", "c"]
delegate: Item {
property int margin: 20
anchors.left: parent.left // Take all the width
anchors.right: parent.right
height: 40 // The height is 0 by default
Rectangle { // Background under the text only. Will not fill the whole row
color: "gray"
anchors.centerIn: parent
width: text.width
height: text.height
}
Text {
id: text
text: modelData
anchors.centerIn: parent
}
}
}
It is not usual to bind delegate's width to its content (childrenRect). In most cases you need parent's width and fixed (or in rare cases dynamic) height. Check out this example - I use anchorsto adjust width and fixed heightof 80:
anchors {
left: parent.left
right: parent.right
}
height: 80
You may also be interested in Label positioning inside this component.
Related
Qt 5.10.1 under Windows 7. I'm trying to anchor some Components inside an Item with margins defined. I mean, I want to anchor taking in account for margins.
My code:
Item {
width: parent.width
anchors.margins: 100
Rectangle {
width: 50
height: 50
anchors.right: parent.right
}
}
I would expect the Rectangle will be positioned on the right side but 100 px from the edge. Instead it's placed just to the edge.
Of course I can add:
anchors.rightMargin: 100
but I have to do this manually for each children of the main Item. I wonder if there's a way to anchor taking care of existing margins.
If I understand well, your problem is not the position of the Rectangle but the position of the parent Item.
Since you define the width of the Item instead of using explicit anchors, the margins has no effect.
Try to position the Item using anchors instead of width:
Item {
anchors.fill: parent
anchors.margins: 100
Rectangle {
width: 50
height: 50
anchors.right: parent.right
}
}
The Item will be correctly positionned 100px from its parent and the Rectangle will be positionned at the edge of the Item.
Note that there is no "CSS padding - like" behavior in QML: you have to explicitely define in each child component how it fills the parent.
Edit (following your comment):
If using inside a Row or Column, as far as I know the only solution is to specify a rightMargin in every child.
About padding:
Padding does not exist in QML (except for Qt Quick Controls 2 components): declaring an item as a child of another item does not mean that it is visually located inside its parent. Thus the only way to position an element is by using anchors on every child.
If you want to simulate a padding in the parent item you can define it as a property and use it in every child:
Item {
readonly property int padding: 100
width: parent.width
height: parent.height
Rectangle {
width: 50
height: 50
anchors {
right: parent.right
margins: parent.padding
}
}
}
Or wrap the children in another Item:
Item {
width: parent.width
height: parent.height
Item {
anchors.fill: parent
anchors.rightMargin: 100
Rectangle {
width: 50
height: 50
anchors.right: parent.right
}
}
}
I tried to enable scrolling of the content in my application using a Flickable.
Now content is scrollable, but content hides the top content.
Code:
Flickable {
anchors.top: pageMessagesHeader.bottom
anchors.topMargin: 20
boundsBehavior: Flickable.DragOverBounds
contentHeight: contentItem.childrenRect.height
contentWidth: contentItem.childrenRect.width
height: 500
width: 400
ScrollBar.vertical: ScrollBar {}
Rectangle {
color: "white"
height: 1000
width: 400
id: listMessages
}
}
Here you can find a sentence:
Flickable does not automatically clip its contents. If it is not used as a full-screen item, you should consider setting the clip property to true.
Which is the solution.
Just add
clip: true
to your code, and you'll be good.
Clipping has some perfomance disavantages which can greatly affect the application as it grows. Hence, its usage, especially outside the views scenario, should be evaluated carefully.
ScrollView clips all its contents to its size. Is it possible to make it work only for top and bottom but allow children to go out of parent's frame on the right and on the left?
If what you want is to only scroll in one direction, then just set the width/height of the content item to the width/height of the ScrollView, using property bindings (because items inside ScrollView are reparented to ScrollView.contentItem). The below example will scroll only vertically. I've tested it, if you need confirmation that it actually works.
Item {
ScrollView {
id: scrollview1
anchors.fill: parent
anchors.margins: 20
clip: true
ColumnLayout {
width: scrollview1.width
}
}
}
I can only imagine one reason, why you don't set the ScrollView's width to a higher value (the contentItem's width).
To be able to do so, while not constraining the ScrollView in it's width, you can use a simple trick:
Item {
id: limitedWidthItemToAnchorTo
width: 200 // the width the ScrollView was supposed to have
height: 400
ScrollView {
width: contentItem.width + __verticalScrollBar.width// Don't limit the width.
height: 400 // Limit only the height.
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff // You don't need them.
contentItem: Rectangle {
width: 700 // This will set the ScrollViews width.
height: 600 // The height will be clipped by the ScrollView.
// You can scroll though.
gradient: Gradient {
GradientStop { position: 0; color: 'red' }
GradientStop { position: 1; color: 'blue' }
}
border.width: 10
}
}
}
You'll wrap it in the Item, anchor to this, and you'll be good.
Alternatively you could use masks, but this would be... more complicated.
Per se it is not possible to clip only horizontal or vertical, as the clipping is done using the Item's bounding box.
I have a ColumnLayout, which has its anchor set to anchor.fill: parent, therefore it has already a set dimension, which depends on its parent dimension.
How can I add Rectangles into this ColumnLayout, with a specific spacing from top to bottom?
Right now I have this:
ColumnLayout {
anchors.fill: parent
spacing: 2
Rectangle {
height: 50
width: 50
color: "red"
}
Rectangle {
height: 50
width: 50
color: "green"
}
Rectangle {
height: 50
width: 50
color: "blue"
}
}
But instead of having the rectangles from top to bottom with a spacing 2 it layouts the Rectangles evenly in the ColumnLayout.
One solution would be to anchor the first rectangle to the parent's top and the anchor the rest of the rectangles one after the other, but I would like to avoid this if possible.
Differently from previous positioners, such as Column or Row, layouts were introduced to support graphical scaling of UI, also by filling available space (in this specific case fill their parent). In that sense the spacing property should not be seen as a strict upper bound to the spacing between Items but as the minimum allowed distance between them.
The current approach to solve your issue is to use a "filler" Item, which uses the fillHeight property. This Item occupies all the space left by the other Items inside the layout thus packing them together, as needed:
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 100
height: 200
ColumnLayout {
anchors.fill: parent
spacing: 2
Rectangle {
height: 50
width: 50
color: "red"
}
Rectangle {
height: 50
width: 50
color: "green"
}
Rectangle {
height: 50
width: 50
color: "blue"
}
Item { Layout.fillHeight: true } // <-- filler here
}
}
Note that you can exploit the same approach and add a filler at the beginning of the layout to vertically center the children Items. Finally, note that in this case it would be advisable to use a Column which lays down the Items correctly as expected, disregarding the available space left.
Just make your choice.
It should be noted that while this approach works Layouts provide a lot of properties to control the size of the Items. Please refer to the other answer for some insight on the subject.
The accepted answer is one valid approach, however, there are others.
1) ColumnLayout decides its own height
If you are simply trying to place items in a column from the top downwards, then there is no need to force the height of the ColumnLayout.
Instead of
anchors.fill: parent
use
width: parent.width
and let the ColumnLayout size itself to fit its contents, as below:
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 100
height: 200
ColumnLayout {
width: parent.width
spacing: 2
Rectangle {
height: 50
width: 50
color: "red"
}
Rectangle {
height: 50
width: 50
color: "green"
}
Rectangle {
height: 50
width: 50
color: "blue"
}
}
}
2) ColumnLayout resizes the items to achieve desired spacing.
If there are too many or too few items to perfectly fill the layout, you can allow the layout to resize the items (instead of resizing the spacing).
The following attached properties control how the layout treats your items, when deciding what can be stretched or shrunk in order to fit the layout:
Layout.preferredHeight
Layout.minimumHeight
Layout.maximumHeight
Layout.fillHeight
Example, where Rectangles are enlarged slightly to achieve the desired spacing:
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 100
height: 200
ColumnLayout {
anchors.fill: parent
spacing: 2
Rectangle {
Layout.fillHeight: true
Layout.preferredHeight: 50
width: 50
color: "red"
}
Rectangle {
Layout.fillHeight: true
Layout.preferredHeight: 50
width: 50
color: "green"
}
Rectangle {
Layout.fillHeight: true
Layout.preferredHeight: 50
width: 50
color: "blue"
}
}
}
In this code, when I click zoomIn rectangle, the picture does zoom in, but the default scrollbars of ScrollView do not change accordingly. They remain the same. Therefore I cannot scroll the zoomed picture.
Please point out the fault.
Rectangle {
id: zoomIn
height: 50; width: 50; color: "blue"
Text { text: qsTr("Zoom In") }
MouseArea {
anchors.fill: parent
onClicked: {
currentPicture.scale += 0.5
}
}
}
ScrollView {
id: head
anchors.top: buttons.bottom
anchors.topMargin: 30
height: 300; width: 300
frameVisible: true
Image {
id: currentPicture
height: head.height; width: head.width
source: folderModel1.hh
z:0
}
}
The scale property doesn't affect the width and height of the item that it's applied to:
A scale of less than 1.0 causes the item to be rendered at a smaller size, and a scale greater than 1.0 renders the item at a larger size.
ScrollView depends on the width and height of the contentItem:
The width and height of the child item will be used to define the size of the content area.
Since you're upscaling the image anyway, you can probably just increase the width and height instead of the scale.
Why is the scale property there if we can bypass it without any side effects?
They have different purposes. One example that I can think of is animations in a grid. Take, for example, the inventory grid in a game. The items fit within the grid, and the grid expects them to all be the same width and height. If you want to animate a mouse-over effect that makes an item larger as it's hovered by the mouse, you can use the scale so that you don't affect the actual width and height. So, layouting could be one reason.