How do you select a character from text in Qt? - 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.

Related

Add hint to QML TextField with maximum length?

I have a QML TextField where the user can input a limited amount of characters. Is there a way to add a simple hint like so? It can be inside the input too (as uneditable text, of course), or next to it.
ColumnLayout {
Label {
text: "Short description"
}
TextField {
Layout.fillWidth: true
maximumLength: 30
}
}

Partially deleting old text of QML::Text while updating it with new text

I have the following QML Text embedded inside a Rectangle
Rectangle {
id: textContainer
border.color: "black"
Text {
id: myTextView
anchors.fill: textContainer
anchors.margins: 10
font.pointSize: 4
wrapMode: Text.WordWrap
Connections {
target: cpp_controller
onUpdateCalledFromCppCode: {
myTextView.text = message + myTextView.text
}
}
}
}
onUpdateCalledFromCppCode gets called like twice or thrice in a second which updates myTextView::text field. This works very well.
Question:
I want to delete the older text. Like if the text is more that 10 lines, I want to delete the 11th and the rest of it. This is to show a continious update making it look like rolling down with updated text all the time. Is there a standard way to do this in QML?
Adding clip: true does the trick I was looking for.
Thus, my Text QML item displays only what could be made visible within the Text QML item area and updates the latest in the visible area. Note, in the question I posted . I am prepending the new text to Text:text not appending.

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.

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).

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