Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wrap some text in a rectangle in QML?

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?

like image 460
Laurent Michel Avatar asked Dec 20 '22 05:12

Laurent Michel


2 Answers

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.

like image 105
BaCaRoZzo Avatar answered Dec 30 '22 01:12

BaCaRoZzo


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

like image 20
Senyai Avatar answered Dec 30 '22 02:12

Senyai