I would like to get the real width & height of a Text element. If I use the paintedWidth & paintedHeight properties, I don't get it. For example:
Rectangle {
color: "green"
width: a_text.paintedWidth
height: a_text.paintedHeight
Text {
id: a_text
text: "r"
color: "white"
anchors.centerIn: parent
}
}
If I run that code, I see some green space on the left of the "r" on top of the "r" and below the "r". So I'm not getting the real width & height of the Text. I mean, the width & height of the white pixels.
Any way to do it?
P.S. Also I would need the relative position of the real top-left corner of the text, I mean, the x & y of the top-left white pixel relative to the "official" top-left pixel of the Text.
The solution is to use the new TextMetrics element (requires QtQuick 2.5), and its tightBoundingRect property, to get the real width & heigth of the foreground text:
import QtQuick 2.5 // required!
Rectangle {
color: "green"
width: t_metrics.tightBoundingRect.width
height: t_metrics.tightBoundingRect.height
Text {
id: a_text
text: "r"
color: "white"
anchors.centerIn: parent
}
TextMetrics {
id: t_metrics
font: a_text.font
text: a_text.text
}
}
"your_text_object.paintedHeight" or "your_text_object.paintedWidth": is what you need to get the height of the text, including height past the height which is covered due to there being more text than fits in the set height.
Text QML Element paintedHeight
Related
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.
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
}
}
}
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.