Qml: use Text's elide property with textFormat: RichText - qt

I have an RSS feed with escaped HTML characters that I want to display in a Text component where I trim the excess content with elide: Text.ElideRight and wrapMode: text.WordWrap.
While this works very well for plain text, when I use textFormat: Text.RichText the trimming does not work.
How can I make the trimming to work or, if this is impossible, encode the HTML easily prior to binding it to the text component?

Indeed Text doesn't support elide for Text.RichText.
There is a bug open on the Qt bug tracker, and after the first reply there is a possible solution, that I copy and paste here for an easy read:
TextEdit {
property string htmlText: "<b>"+workingText.text+"</b>"
text: htmlText
width: parent.width
onHtmlTextChanged: {elide();}
onWidthChanged: elide();//Yes, this will be slow for dynamic resizing and should probably be turned off during animations
function elide(){//Also, width has to be set, just like elide, or it screws up
text = realText;
var end = richText.positionAt(width - 28,0);//28 is width of ellipsis
if(end != realText.length - 7)//Note that the tags need to be taken care of specially.
text = realText.substr(0,end + 3) + '…' + '</b>';//3 is + <b>
}
font.pixelSize: 22
}

I figured a simple workaround which works quite well in my case, where the width and height of the Text component are fixed, i.e. trimming the string manually. The end result depends a bit on how monospace your font is.
text: {
var name = "my very very long text string with HTML markup that goes on forever and ever and ever"
var text = name.substring(0,45)
if (name.length > 45) text += "..."
return text
}

Related

How to check the auto alignment of text when Text contains Arabic words

I have created a Rectangle item with Text item in center which acts as Edit box with Cursor item at the end of the text.
So for Orientation to be taken care by QML, I have modified the Text item as
Text
{
id: text_input
font.bold: true
font.pixelSize: 22
color: "white"
text: view.defaultTextField
elide: Text.ElideLeft
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
maximumLineCount: 1
clip: true
anchors{
rightMargin: 10
leftMargin: (textInputField === "") ? 18 : 12
verticalCenter: parent.verticalCenter
}
}
and Cursor image as
Image
{
id: img_cursor
x: (textInputField !== "") ?
(text_input.x + text_input.contentWidth)) : 12
anchors.verticalCenter: parent.verticalCenter
source: "text_cursor.png"
}
Now if textInputField contains Arabic text, TextItem is auto change the orientation from right to left. and english it is changing to start from Left.
Text appending coming in :
Arabic: Left <--Right
English: Left --> Right
But for Cursor position, how i can make the logic to auto detect and change the x position of the cursor based on the text_input orientation direction (Arabic and English).
Arabic isn't always RtoL. Numbers, for example, are written LtoR (just like in English). Also, foreign words would be written LtoR. Conversely, if you add an Arabic word in an English text, text direction will change somewhere. Might be the middle of the line, might be at either end.
That's why a simple trick like calling QFontMetrics.width() will only work for simple cases.
Try QTextLayout instead. QLineEdit uses this code in it's control to figure out the X position of the cursor:
qreal cursorToX(int cursor) const { return m_textLayout.lineAt(0).cursorToX(cursor); }
I created a function to check the alignment of the text. so on arabic change , the Text orientation will be modified automatically.
function isArabicAlignment() {
if(text_input.horizontalAlignment === Text.AlignRight)
return true;
else
return false;
}
So on Text input modifies , i will check the condition and update the cursor position.
x: (textInputField !== "" && isArabicAlignment()) ?
(text_input.x + text_input.contentWidth)) : //Changing Cursor in reverse.

How do you select a character from text in Qt?

I've uploaded a text file and parsed the content into a text object. Eventually I will be passing each character in the string to a rectangle of its own. I am trying to figure out how to pick a character from the string in qml?
For example:
//Assume the text is already parsed from the file and it is stored in the object below
Text {
id:myText
text: "The quick brown fox jumps over the lazy dog"
visible:false
}
// And now i would like to transfer the first letter into a rectangle
Rectangle{
id: firstRect
width:rectText.width
height:rectText.height
color: "yellow"
Text {
id:rectText
text: // How do I set this as the first character 'T'
font.pixelSize: 14
color: "black"
}
}
How can set the rectangle with the character from myText to rectText ?
Eventually I will be setting each character in a rectangle of its own.
It is close to impossible. At least not by using what little font metrics functionality is provided to QML. There is TextMetrics available since Qt 5.4, but for some reason it didn't report the text size accurately, at least for the fonts that I've been using. It may have to do with this issue. I ended up getting accurate results by appending the 字 character to the queried text, and I don't even want to go into detail how I figured that out.
And then, if that happens to work, you only have the text dimensions, but no way to determine the position of that rectangle, since QML text elements can only give you the cursor position, but not the position of any particular character. If you have a single line of text it could be doable - just calculate the width of the preceding text, but for multi line it is a no-go.
You will probably have to take a very different approach. Maybe implement an adapter that presents strings as list models, and you represent every individual character as a QML element in something like a flow view.
But having a discrete visual item for every character will be huge overhead for long text, so if you are going to have such text, you will also have to handle a model proxy that only displays a particular portion of the text.
I can currently think of no other way to get accurate information about the position and size of text character. The API simply doesn't have that functionality.
There is a simple example that is perfectly applicable for short text:
ApplicationWindow {
id: main
width: 640
height: 480
visible: true
property var charlist: textfield.text.split('')
property int currentChar: 0
Column {
TextField {
width: main.width
id: textfield
text: "example text"
}
Flow {
width: main.width
height: 200
Repeater {
model: charlist
delegate: Rectangle {
Text { id: tt; text: modelData; font.pointSize: 20 }
width: tt.width
height: tt.height
color: "red"
border.color: index === currentChar ? "black" : "red"
MouseArea {
anchors.fill: parent
onClicked: {
currentChar = index
var pos = mapToItem(main.contentItem, 0, 0)
info.text = "rectangle x y w h: " + pos.x + ", " + pos.y + ", " + width + ", " + height
}
}
}
}
}
Text {
id: info
}
}
}
You can enter arbitrary text in the text field, which will be represented by a model view that creates an item for every character. Clicking a character will "select" it and will also give you the rectangle values corresponding to its position in the application window.

Resize rectangle depending on whether text wraps or not QML

So I have a rectangle with a label text inside. If the text is too long to fit in one single line, I want to increase the size of the rectangle which should otherwise remain the same.
So far I tried using lineCount, clip, truncated properties from
the qt docs-
The text doesn't fit, and I get ellipses. However, Clip and Truncated always return false. Line count just returns the current line count ignoring wheter it should take more space or not.
Now I'm trying to use contentWidth from here. However this always returns a value that is equal or lesser than the actual width. I thought this should return the total value that it should occupy?
How can I accomplish this?
EDIT
I'm trying something like this, but no matter how long my text is or how truncated it is the content width is always smaller than the width of the label. I got the impression from the qml documentation that contentWidth will take into account even the omitted text.
Rectangle{
id: rec
...
Label{
id: messageText
height: Format.singleLine
text: "this text is very long and should be two lines"
Component.onCompleted: {
if (contentWidth > width){
rec.height = Format.multipleLines
}
}
}
}
SECOND EDIT
I learned that the reason why lineCount was always one was due to creating the object programatically (myRectangle is the rectangle containing the label):
messages.source = Qt.resolvedUrl("myRectangle.qml");
messages.item.message = message;
After the first line the Label was created label was intiliazed with lineCount 1. Then I'll try to change the text which will only be truncated after this point.
How about something like this:
height: lineCount > 1 ? Format.multipleLines : Format.singleLine
If your Rectangle contains only your Label, you could do something like this relying on the wrapMode property of the Label, and the childrenRect.height property of the Rectangle.
Rectangle {
anchors.centerIn: parent
width: 200
height: childrenRect.height
border.width: 1
Label {
id: messageText
width: 200
wrapMode: Text.Wrap
text: "This text is very long very long. Like way too long to fit on a single line."
}
}
Alternatively, if you don't mind having your label contain the rectangle instead as the other way around, you could do:
Label {
id: messageText
anchors.centerIn: parent
width: 200
wrapMode: Text.Wrap
text: "This text is very long very long. Like waaaaay too long to fit on a single line."
Rectangle {
anchors.fill: parent
z: -1
border.width: 1
}
}
I'm not sure if there is a better way to do this, but this code did it for me (this is inside Label):
onContentWidthChanged: {
if((lineCount === 1) && (contentWidth !== 0)){
rec.height = Format.singleLine;
}
else{
rec.height = Format.multipleLines;
}
}
I initialized the height to be Format.multipleLines. Then once the text is accounted for (ContentWidth is not 0) I check if lineCount is one. If so I modify the height to be be format.singleLine.
Note that onContentWidth gets call twice after the component is created. The first time contentWidth will always be 0 (hence the need to check for it).

Can anyone teach/guide me how to get string length of a list button in Qml?

Can anyone teach/guide me how to get string length of a list button in Qml? I am in need of list button text's length/width in characters instead pixelWidth.
Ok, let's say your question was:
How can I get a string length with specified font fits a button with
specified size?
In this case the answer could be:
I think you should not treat a string like a set of chars. A font could be fixed-size (Monospaced), where all the characters have the same width or variable-width where the letters have different widths. So pixel width of string "iii" could be differ from string "www" although both of them have 3 characters. Therefore, I guess the best practice is to trim entire string at position depending on the needed width. You can use FontMetrics, for example:
Button {
id: btn
width: 300
text: ""
font.pixelSize: 50
anchors.centerIn: parent
Component.onCompleted: {
var str = "Some long long long long long string";
var btnString = fnt.elidedText(str,Qt.ElideNone, btn.width);
btn.text = btnString;
}
}
FontMetrics {
id: fnt
font: btn.font
}

Detect text content size in TextField

I'd like to reduce the font size in case user types a long text in a TextField. Is there a way to know how wide the current text is rendered?
As far as I know, it is not possible to do directly. You could, however, cheat by using an invisible item that contains the properties you need:
Text {
id: hiddenText
anchors.fill: tf
text: tf.text
font.pixelSize: tf.font.pixelSize
visible: false
}
TextField {
id: tf
width: 100
height: 60
font.pixelSize: 25
onTextChanged: {
while ((hiddenText.contentWidth > hiddenText.width) || (hiddenText.contentHeight > hiddenText.height)) {
font.pixelSize -= 1
}
}
}
You would have to do the same for scaling up the text, in case the user erases some input. The Text component does not contain borders, so you have to decrease it's width by a few pixels to make it the same size as the TextField as well.
Also, check out TextArea or TextInput. They may fit your need.

Resources