TextArea, process each character one at a time - qt

I have a text area and I want to listen to every characters inputted on it,
What I am trying to do is when a spacebar is pressed, I will perform an operation or processing such as replacing some characters on it.
I have tried and listen to onTextChanged however, it may cause recursion as setting my TextArea.text to new value triggers a call to onTextChange event thus causing Stack Overflow.
I have tried to listen to Keys.onPressed event as well and listen to space key but the space key is processed as well and when I set a new text value, the space value is forcedly inserted.
How and where should I handled and listen to these events?
How can I listen to each inputted characters in QML text area, catch the space bar, process, and replace the text with the new processed text? Also ignore the last spacebar entry?
How do I reject a chracter when inputted in the TextEdit or TextArea?

Can you use TextInput which helps you differ whether text is changed by user or programmatically ?
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
TextInput
{
width: 200
height: 100
onTextEdited:
{
if( text[text.length-1] === " " )
{
text = process(text)
}
}
anchors.centerIn: parent
}
function process(textinput)
{
return textinput.toUpperCase()
}
}

Related

Qml TextEdit Custom key event not processed properly

I wanted to add custom key events to a TextEdit. But, it seems key events are not properly processed inside TextEdit.
For example, in the code below, I am trying to handle Space key events. Although the Space keypress is recognized by the signal handler function, the output text does not contain a space. It is the same for all other key events. How do I overcome this?
import QtQuick 2.15
import QtQuick.Controls 2.15
Item{
function processSpace(event){
event.accepted = true
console.log(xTextEdit.text)
}
TextEdit{
id: xTextEdit
height: parent.height
width: parent.width
Keys.onSpacePressed: processSpace(event)
}
}
You accept the event, and thus prevent the default handling.
Set event.accepted = false instead, so the event will be propagated.
Note that accepted is by default true (at least for key events), so not setting it will the accept the event

Text Selection in QtQuick

I am writing a dialog box for a software plugin for Cura, a 3D printing slicer. When the user slices their file it brings up a dialog box to name the file before uploading it to the 3D printer. A python script generates the name in the format "print name - material-otherinformation.gcode" Right now, when the dialog loads, it highlights the whole text field except for the .gcode extension at the end. I would like it to only highlight a portion of that text field by default, namely the print name part. It is easy for me to return the length of that section as an integer and pass it to the QML file. I am definitely an amateur with QML, but it seems like the select function should be able to handle that but I can't figure out the usage. Any assistance or pointers would be much appreciated!
Here's a simplified version of the code. What I would like to do is add something to this so that just "word 1" is highlighted when the dialog box appears.
import QtQuick 2.1
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.1
import UM 1.1 as UM
UM.Dialog
{
id: base;
minimumWidth: screenScaleFactor * 400
minimumHeight: screenScaleFactor * 120
Column {
anchors.fill: parent;
TextField {
objectName: "nameField";
id: nameField;
width: parent.width;
text: "word1 - word2 - word3.gcode";
maximumLength: 100;
}
}
}
It's just a matter of when to use the TextInput::select() method. This could be in Component.onCompleted: of either the dialog or the text field, for example:
UM.Dialog
{
...
property int selectionLength: 0
Component.onCompleted: nameField.select(0, selectionLength);
...
TextField {
id: nameField;
text: "word1 - word2 - word3.gcode";
}
...
}
If selectionLength could change after dialog is created, then I'd create a separate function which can be called from different events or even directly:
UM.Dialog
{
...
property int selectionLength: 0
Component.onCompleted: select(selectionLength);
onSelectionLengthChanged: select(selectionLength);
function select(len) { nameField.select(0, len); }
...
TextField {
id: nameField;
text: "word1 - word2 - word3.gcode";
}
...
}
Obviously if the selection isn't going to be from the first character then some adjustment to this strategy will be needed.

Using variable as text in Text element immediately activates onTextChanged

If I have the following code:
import QtQuick 2.10
import QtQuick.Window 2.10
Window {
id: app
visible: true
width: 640
height: 480
property bool txt: false
Text {
text: app.txt
onTextChanged: { console.debug("Text changed") }
}
}
I get “Text changed” displayed in my console as soon as the app loads, however if I set the text manually to something like
Text {
text: "Some text"
onTextChanged: { console.debug("Text changed") }
}
I don’t get the “Text changed” display in my console unless I actually have something that changes the text after the app loads.
Is this normal behaviour? Is there a way to use the variable as the text but not have onTextChanged activate as soon as the app loads?
Yes, it's normal behaviour for qml, becuase your first text property is "" (nothing), and when you give it a variety it changes from "" to app.txt. If you set manually text, in this case, there is nothing to change.

Auto-Scrolling TextArea in QML Sailfish / Silica

Update: Possible solution at the bottom.
I am learning App development for Sailfish with very little prior experience in Qt/QML but some experience in other Toolkits (GTK, Tk, Motif, Xaw).
I am currently writing a dumb chat client (no protocoll, just send text over network.) I have the Talk/Chat page defined in QML as follows:
import QtQuick 2.0
import Sailfish.Silica 1.0
Page {
id: talkPage
PageHeader {
id: header
title: qsTr("Talk")
width: parent.width
anchors.top: parent.top
}
TextField {
id: message
placeholderText: qsTr("Type your Message...")
width: parent.width
anchors.bottom: parent.bottom
EnterKey.onClicked: {
log.text += "\n> " + text;
text = "";
}
}
TextArea {
id: log
readOnly: true
wrapMode: TextEdit.Wrap
width: parent.width
anchors.top: header.bottom
anchors.bottom: message.top
text: qsTr("Talk log")
}
}
You can now enter text in the message TextField and hit enter to add it to the log TextArea.
Question: How to I make the TextArea auto-scroll to the bottom each time a message is added to it.
Note that if I understand correctly, this is using Sailfish.Silica TextField, and this is different from standard (QtQuick.Components ?) So I cannot use log.append("\n> " + text); from QtQuick.Controls (which isn't available on Sailfish.)
A sollution in using C++ rather than Javascript/QML is fine to, since I'll need it to handle Networking anyway.
Thanks in advance for any suggestions!
Bonus Question: I tried arranging header, message and log in a Column previously, but didn't know how to make header and message keep their default height but make log expand to fill the screen. In GTK there is an expand property for this. Any hints?
Possible Solution: Put the TextArea inside a SilicaFlickable. For some reason TextArea is already scrollable without this extra step, but this way one can use scrollToBottom() and force the desired behavior. As a bonus, in this way we can add a nice scrollbar.

QML - Dynamically swap the visibility/opacity between overlapping Text and TextArea

I want to have a widget in QML which has combination of the following behaviors:
1) Multi line edit
2) Auto scroll the content as and when I hit newline. (The content on top of the page keeps going up as and when I enter new content at the bottom)
3) Have a placeholder text functionality.
As far as I know, only Text and TextField are having placeholder text property and only TextArea is having a multi line edit plus auto scroll. But If there is any such widget with all the combinations then, my question “Dynamically swap the visibility/opacity between overlapping Text and TextArea “ would be invalid.
In case there is no such widget (I wonder why), I am thinking to have a rectangle which has both Text and TextArea overlapping and based on the below logic I want to have the visibility/opacity/focus on one of them:
If the Text Area is empty (0 characters), then have the Text in the foreground with focus and with the placeholder text “Enter some text”. But as soon as the user starts typing, the Text should lose the focus, opacity and go to background and the TextArea should gain the focus and come to the foreground and start accepting multi line input. Similarly, when TextArea is in the foreground and is empty (0 characters) and when the user click on any other widget outside my Rectangle, the Text should again gain the focus, come to the foreground and display the placeholder again.
I tried to get inspiration from this code, but failed miserably, it would be helpful if anyone can help me with a few lines of code or give me some pointers on how to solve this.
You can implement placeholderText for TextArea the same way Qt does in TextField. The source can be found here: TextField.qml
When you remove all the comments and properties, you basically have a background and on top of that a MouseArea, the placeholderText Text and a TextInput. Since you need to have the placeholder visually below the TextArea, you must have a transparent background:
PlaceholderTextArea.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
Rectangle {
property alias placeholderText: placeholder.text
id: background
width: 640
height: 480
color: "#c0c0c0"
Text {
id: placeholder
anchors.fill: parent
renderType: Text.NativeRendering
opacity: !textArea.text.length && !textArea.activeFocus ? 1 : 0
}
TextArea {
id: textArea
anchors.fill: parent
backgroundVisible: false
}
}
and use your component:
PlaceholderTextArea {
placeholderText: qsTr("Hello World")
anchors.fill: parent
}
Here's an alternative implementation, that works a bit better for me:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Item
{
property alias placeholderText: placeholder.text
property bool __shouldShowPlaceholderText:
!textArea.text.length && !textArea.activeFocus
// This only exists to get at the default TextFieldStyle.placeholderTextColor
// ...maybe there is a better way?
TextField
{
visible: false
style: TextFieldStyle
{
Component.onCompleted: placeholder.textColor = placeholderTextColor
}
}
TextArea
{
id: placeholder
anchors.fill: parent
visible: __shouldShowPlaceholderText
activeFocusOnTab: false
}
TextArea
{
id: textArea
anchors.fill: parent
backgroundVisible: !__shouldShowPlaceholderText
}
}

Resources