In a QML code, I want to put a label containing capitalised words inside an image. The label is at the bottom of the image and I want the bottom of the text to match with the edge of the image (for example, like the graph paper from a diary). By making the bottom of both match with
anchors.bottom: myImage.bottom
I can't avoid having some space between the letters and the edge of the image.
I have simplified it into an example as follows:
Rectangle {
id: content
width: 300
height: 300
color: "blue"
Label {
text: "ABC"
color: "black"
font.pixelSize: 200
anchors.bottom: content.bottom
anchors.horizontalCenter: content.horizontalCenter
background: Rectangle{
color: "green"
}
}
}
And I can see that there is a green space between the letters and the bottom of the blue rectangle.
Is there a simple way to fix this other than messing with the value of anchors.bottomMargin
?
Based on the documentation, there are four vertical anchor lines for every visible Item
: top
, verticalCenter
, baseline
, and bottom
. If the component is not a Text
component, the baseline
is the same as the top
anchor. [1]
For a Text
component, assuming a standard font and the initial size of the Text
, the baseline
is where the text sits, with characters like 'A' and 'B', placed exactly on top of the baseline
. The baseline is illustrated in the below image.
The "empty space" in the OP's question is actually the space between the baseline
and the bottom of the Text
item, which can be used for anchoring. As shown in the following code:
Label {
anchors.baseline: parent.bottom
}
The baseline position can also be retrieved from baselineOffset
, which can also be used for positioning the text.
This code generates the image above.
Label {
id: label
text: 'AxyÁ'
font.family: 'Times New Roman'
font.pixelSize: 100
background: Rectangle { color: '#fed' }
Repeater {
model: ['top','baseline','verticalCenter','bottom']
Rectangle {
anchors.top: parent[modelData]
height: 1; width: parent.width; color: Qt.hsva(index/4,.5,.8)
Text {
color: parent.color; text: modelData; padding: 5;
anchors { left: parent.right; baseline: parent.top }
}
}
}
}
But it doesn’t end there. This approach will likely clip some glyphs of your Text
, such as 'g', 'y', and 'p', where parts of the characters extend below the baseline.
To fix this, we can use states
to change the anchors based on characters that might extend below the baseline. The following code checks the text for the presence of yqpjg
, and if any of these characters are found, it adjusts the anchor accordingly.
Rectangle {
color: '#fed'
Text {
id: label
text: 'ABC'
font.pixelSize: 100
anchors.horizontalCenter: parent.horizontalCenter
states: [
State { when: !label.text.match('[yqpjg]');
AnchorChanges { target: label; anchors.baseline: parent.bottom } },
State { when: label.text.match('[yqpjg]');
AnchorChanges { target: label; anchors.bottom: parent.bottom } }
]
//** Or just use the baselineOffset. **
// x: (parent.width - width)/2
// y: parent.height - (label.text.match('[yqpjg]') ? height : baselineOffset)
}
}
As mentioned, the appearance of a character can vary depending on the font. Some fonts may add extra space below characters like 'g', 'y', etc. The recommended lowest position for a glyph, known as the descent, can also differ from the bottom of the glyph or its height.
In QML, we can access these values using FontMetrics.
The following image demonstrates some of these values within a Label
.
This code generates the image above.
Label {
id: label
text: 'AxyÁ'
font.family: 'Times New Roman'
font.pixelSize: 100
background: Rectangle { color: '#fed' }
FontMetrics { id: metrics; font: label.font }
Repeater {
model: [
['overlinePosition', label.baselineOffset - metrics.overlinePosition],
['\nascent', label.baselineOffset - metrics.ascent],
['baseline', label.baselineOffset],
['xHeight', label.baselineOffset - metrics.xHeight],
['strikeOutPosition', label.baselineOffset - metrics.strikeOutPosition],
['\nunderlinePosition', label.baselineOffset + metrics.underlinePosition],
['descent', metrics.descent + label.baselineOffset],
['\nheight', metrics.height],
]
Rectangle {
y: modelData[1] ?? 0
height: 0.5; width: parent.width; color: Qt.hsva(index/7,1,.7,1)
Text {
color: parent.color; text: modelData[0]; padding: 5;
anchors { left: parent.right; verticalCenter: parent.top }
}
}
}
}
As you can see, the ascent
is slightly lower than the top position. In this example, I used Times New Roman, where the descent matches the glyph's height, though the descent can sometimes be higher than the bottom of the Text
.
In conclusion, if you need simple alignment, anchors are sufficient. However, for more advanced control over text positioning, you should use FontMetrics
.