How to wrap some text in a rectangle in QML?

14,448

Solution 1

As far as I know, Font metrics were made available to developers in Qt 5.4, so they are relatively new, in QML. You got mainly FontMetrics and TextMetrics. A simple usage example:

import QtQuick 2.4
import QtQuick.Window 2.2

Window {
    visible: true
    width: 280; height: 150

    TextMetrics {
        id: textMetrics
        font.family: "Arial"
        font.pixelSize: 50
        text: "Hello World"
    }

    Rectangle {
        width: textMetrics.width
        height: textMetrics.height
        color: "steelblue"

        Text {
            text: textMetrics.text
            font: textMetrics.font
        }
    }
}

As noted by Phrogz in the comment below, the TextMetrics type does not support measuring wrapped text.


EDIT

For what is worth I've never ever had the need to use metrics in QML. For me content* or painted* properties served the purpose and, as of Qt 5.12, they seem to work fine. Aka the following two solutions generate the correct visual behaviour:

// solution 1
Rectangle {
    width: myText.contentWidth
    height: myText.contentHeight

    Text {
        anchors.fill:parent
        id: myText
        font.family: "Helvetica"
        font.pointSize: 50
        text:  qsTr("The string I want to display")
    }
}

// solution 2
Rectangle {
    width: myText.paintedWidth
    height: myText.paintedHeight

    Text {
        anchors.fill:parent
        id: myText
        font.family: "Helvetica"
        font.pointSize: 50
        text:  qsTr("The string I want to display")
    }
}

I would prefer those solutions to the usage of metrics for such a simple use case as the one proposed by the OP. For the opposite case - fitting a text in a specific size - a combination of properties can do the trick, e.g.:

Rectangle {
    anchors.centerIn: parent
    width: 200
    height: 30

    Text {
        anchors.fill: parent
        text: "Wonderful Text"
        minimumPixelSize: 2
        fontSizeMode: Text.Fit
        font.pixelSize: 200
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
    }
}

Here the pixel size is simply over the top but the text still fits because a minimum size of 2 is set and the text has a clear fitting policy and clear boundaries, defined by the anchoring.

Solution 2

I'm sure Label component will do the job:

import QtQuick 2.1
import QtQuick.Controls 2.4

ApplicationWindow {
    visible: true

    Column {
        Repeater {
            model: [
                {"color": "red", "radius": 1},
                {"color": "green", "radius": 2},
                {"color": "blue", "radius": 3}
            ]
            Label {
                padding: 0
                text: modelData.color
                font.family: "Helvetica"
                font.pointSize: 50
                background: Rectangle {
                   color: modelData.color
                   radius: modelData.radius
                }
            }
        }
    }
}

enter image description here

Share:
14,448
Laurent Michel
Author by

Laurent Michel

Updated on June 04, 2022

Comments

  • Laurent Michel
    Laurent Michel about 2 years

    I have to perform a very simple task: I want to display a piece of text inside a rectangle and the size of that rectangle should precisely be the width of the text.

    In C++, it's fairly easy to do. Just define the QString and apply the QFontMetrics to get its width. Then define the rectangle graphics element to have that size. It's done within five minutes.

    I have heard that QML is easier to use. Therefore, I was expecting to solve that problem in less than five minutes. I didn't, and I'm still stuck at it. Here's what I have tried:

    Rectangle {
        width: myText.contentWidth
        height: myText.contentHeight
    
        Text {
            anchors.fill:parent
            id: myText
            font.family: "Helvetica"
            font.pointSize: 50
            text:  qsTr("The string I want to display")
        }
    }
    

    This doesn't work for some reason I don't understand. I have found a way to do it in a way that doesn't exactly suits my needs:

    Rectangle {
        width: 100
        height: 100
    
        MouseArea {
           id: myMouseArea
           anchors.fill: parent
           onClicked: parent.width=myText.contentWidth 
           hoverEnabled: true
         }
    
        Text {
            anchors.fill:parent
            id: myText
            font.family: "Helvetica"
            font.pointSize: 50
            text:  qsTr("The string I want to display")
        }
    }
    

    In this case, when I click the rectangle, it gets the correct width. Nevertheless, I am not interested in this solution, because I don't want to have to click to get a rectangle with the correct size.

    I want that the rectangle's size gets the correct size whenever myText changes text. The use of onTextChanged in the Text item doesn't work either.

    What am I missing here?