QML TextArea onEditingFinished On Enter - qt

The QML TextArea inherits onEditingFinished From TextEdit where it is triggered on losing focus or an enter/return press. (assuming no input validation)
However one cannot trigger onEditingFinished from a TextArea with Enter unlike for TextEdit as it is captured for a line break.
Additionally in a window with a single field it can be a unintuitive to remove focus from the TextArea as background and most other Controls don't accept focus so the user has to click off the window or clicking the menu bar.
How can I improve the user experience for triggering actions once they have edited a multiline string input field? Is linebreaks inputted with Ctrl+Enter, with editing completion on Enter an option, and if so how would this be implemented?
Here is the example QML of this scenario:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
Window {
visible: true
width: 640; height: 480
title: qsTr("Hello World")
Column {
TextArea {
implicitHeight: 200
placeholderText: qsTr("Enter description")
onEditingFinished: console.log("Editing complete")
}
Label {text: qsTr("Label")}
}
}

You can override the Return key press event and handle it however you want. If you want to use the Ctrl+Return, check the modifiers property in the event.
TextArea {
implicitHeight: 200
placeholderText: qsTr("Enter description")
onEditingFinished: console.log("Editing complete")
Keys.onReturnPressed: {
if (event.modifiers & Qt.ControlModifier) {
// Ctrl+Return
editingFinished();
}
else {
// Ignore other cases
event.accepted = false;
}
}
}
Or if you want to use the Return key without pressing Ctrl, then you can simply do this:
TextArea {
implicitHeight: 200
placeholderText: qsTr("Enter description")
onEditingFinished: console.log("Editing complete")
Keys.onReturnPressed: {
editingFinished();
}
}

You can do it by handling the simple ReturnPressed and ctrl+ReturnPressed or Shift+ReturnPressed event by yourself.
In the code below I've used Shift+ReturnPressed for new line:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
Window {
visible: true
width: 640; height: 480
title: qsTr("Hello World")
Column {
TextArea {
id: text_area
implicitHeight: 200
placeholderText: qsTr("Enter description")
Keys.onReturnPressed: {
if(Qt.ShiftModifier)
console.log("Editing complete")
else {
text += '\n'
text_area.cursorPosition = text_area.length
}
}
}
Label {text: qsTr("Label")}
}
}

Based on the answers by Jarman (https://stackoverflow.com/a/69723941/1581658) and Farshid616 (https://stackoverflow.com/a/69724074/1581658) I came up with this
TextArea {
implicitHeight: 200
placeholderText: qsTr("Enter description")
onEditingFinished: console.log("Editing complete")
Keys.onReturnPressed: {
if(event.modifiers & Qt.ControlModifier) {
insert(cursorPosition, "\n")
}
else {
editingFinished()
}
}
}
This specifically allows for a newline to be inserted at the current cursor when Ctrl+Enter is pressed, and for editingFinished to be called when a non-modified Enter is pressed

Related

QtQuick button shows outside of dialog

I'm trying to make a customizable single button error message dialog in QtQuick, but when I open the dialog the "Ok" button is shown outside of it:
it looks like this
When I open the dialog again, it works normally, the button is shown at the right place.
Here is a minimal working example :
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 2.4
Window {
visible: true
width: 640
height: 480
Button {
text: "Open Dialog"
onClicked: {
dialog.show("This text can be customized.")
}
Dialog {
id: dialog
standardButtons: Dialog.Ok
title: "Error"
modal: true
function show(txt) {
label.text = txt
open()
}
Label {
id: label
}
}
}
}
I'm using Qt 5.11.3 with QtQuick 2.11, build target is Desktop GCC 64 bits.
Is this a bug? What am I doing wrong?
It seems to be a bug in Qt 5.11.3 or in my installation of it on Linux.
Here is the workaround I used, basically recreating the Dialog from a regular Popup :
import QtQuick 2.0
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
Popup {
x: (parent.width-width)/2
y: (parent.height-height)/2
modal: true
closePolicy: Popup.NoAutoClose
property var title: "Error"
property var msg: ""
function show(message) {
msg = message
open()
}
ColumnLayout {
spacing: 30
Label {
Layout.alignment: Qt.AlignLeft
Layout.preferredHeight: 5
font.bold: true
text: title
}
Label {
text: msg
}
Button {
Layout.alignment: Qt.AlignRight
text: "Ok"
onClicked: {
close()
}
}
}
}

How should I start QML files?

I have 4 QML files: MainMenu.qml, AppArea.qml, Result.qml and main.qml.
When my app starts, I want to see first page as MainMenu.qml fullscreen. There is a button (on MainMenu.qml) to start AppArea.qml. When I click the the button, I want to start AppArea.qml as fullscreen new window.
There is a button (on AppArea.qml), when I click that button, I want to show Result.qml but I want to see Result.qml on AppArea.qml, I mean when Result.qml come outs, AppArea.qml will not disappear but Result.qml will appear on AppArea.qml.
There is a button on Result.qml. When I click the button, the Repeater in AppArea.qml will regenerate, because maybe model of Repeater changing like 1, 2, 3, 4.... There is a button on AppArea.qml, when I click the button, I want to open MainMenu.qml as a fullscreen new window like AppArea.qml.
Actually you can think basic: My app is a game like this:
How way should I choose for these jobs?
In addition to the mentioned post, in your case you are using the component from qml file, so you need to load the component first, your main.qml can be like this:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
id: mainWindow
title: "Main window"
visible: true
flags: Qt.Dialog
modality: Qt.ApplicationModal
Loader{
id: mainMenuLoader
}
Component.onCompleted: {
mainMenuLoader.source="mainMenu.qml"
var mainMenu = mainMenuLoader.item.createObject(mainWindow);
mainWindow.hide()
}
}
and your mainMenu.qml can look like this:
import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 2.2
Component {
id: mainMenu
Window {
id:mmenu
title: "Main Menu"
width: 600
height: 600
visible: true
flags: Qt.Dialog
modality: Qt.ApplicationModal
Loader{
id: appAreaLoader
}
Text {
text: "This is mainMenu"
}
Button{
id: loadAppArea
anchors.centerIn: parent
text: "Start Game"
onClicked: {
appAreaLoader.source="appArea.qml"
var appArea = appAreaLoader.item.createObject(mainMenu);
hide()
}
}
}
}
you will need to do the same for successive windows ...etc.
While for result, you need to use a MouseArea:
appArea.qml:
Component {
id: appMenu
Window {
id:appMenuWindow
title: "App Menu"
width: 600
height: 600
visible: true
flags: Qt.Dialog
modality: Qt.ApplicationModal
Loader{
id:anotherLoader
visible: true
anchors.left: appMenuText.left
anchors.top: appMenuText.bottom
width: parent.width/3
height: parent.height/3
}
Text {
id: appMenuText
text: "This is App Area"
anchors.centerIn: parent
}
Button{
id: loadResult
text: "Show Result"
onClicked: {
anotherLoader.source = "result.qml"
anotherLoader.visible=true
}
}
Button{
anchors.right: parent.right
id: loadMainMenu
text: "Open main Menu"
onClicked: {
hide()
//mmenu.show()
anotherLoader.setSource("main.qml")
}
}
}
}
result.qml:
Rectangle{
color: "green"
Text {
anchors.centerIn: parent
id: resultxt
text: qsTr("This is result, Click to close")
}
MouseArea {
anchors.fill: parent
onClicked: { anotherLoader.visible = false
}
}
}

QT 5.7 QML How to control which Control gets the focus within a TabView

I'd like to arrange for a specific control to get the focus within a TabView Tab. Its seems that the first one gets the focus regardless of what I do. I have tried setting focus:false everywhere else but it doesn't work.
Consider the following code. I have a simple column containing two RadioButtons and a TextField. I'd like to arrange for the TextField to always get focus when the tab is selected, but it always goes to the first RadioButton
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
ApplicationWindow
{
visible: true
width: 800
height: 400
TabView
{
anchors.fill: parent
Tab { title: "tab1"; sourceComponent: foo }
Tab { title: "tab2"; sourceComponent: foo }
}
Component
{
id: foo
ColumnLayout
{
spacing: 32
ExclusiveGroup { id: optionGroup }
RadioButton
{
// i always get the focus!!
exclusiveGroup: optionGroup
text: "Click me"
activeFocusOnPress: true
focus: false
}
RadioButton
{
exclusiveGroup: optionGroup
text: "No, click me!"
activeFocusOnPress: true
focus: false
}
TextField
{
// but i want the focus
placeholderText: "type here"
focus: true
}
}
}
}
Press "tab2" to see this,
I tried forcing within TabView by adding,
onCurrentIndexChanged: getTab(currentIndex).item.forceActiveFocus()
But it makes no difference.
I've read the explanation of focus but it hasn't helped in this case.
Thanks for any suggestion or help,
Probably a bug. Try Qt Quick Controls 2.0 instead:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 800
height: 400
header: TabBar {
id: bar
width: parent.width
TabButton {
text: qsTr("tab1")
}
TabButton {
text: qsTr("tab2")
}
}
StackLayout {
anchors.fill: parent
currentIndex: bar.currentIndex
ColumnLayout {
id: columnLayout
spacing: 32
ButtonGroup {
id: optionGroup
buttons: columnLayout.children
}
RadioButton {
text: "Click me"
}
RadioButton {
text: "No, click me!"
}
TextField {
placeholderText: "type here"
focus: true
}
}
}
}

How to programmatically press QML Button?

I am building a dialog in QML. I have some TextField, but I'd like that if the user presses enter (accepted signal is emitted), the id: okButton is pressed, actually activating it visually for a moment.
I see that the pressed property is read only.
Thanks!
You can simply call clicked() signal for simulating button press:
Keys.onReturnPressed: {
clicked()
event.accepted = true
}
You can make it checkable for a short duration while you emulate the click with the checked property:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
Timer {
id: timer
running: true
repeat: true
interval: 100
onTriggered: {
button.checked = false;
button.checkable = false;
}
}
Row {
TextField {
anchors.verticalCenter: parent.verticalCenter
onAccepted: {
button.checkable = true;
button.checked = true;
timer.start();
}
}
Button {
id: button
text: "Submit"
anchors.verticalCenter: parent.verticalCenter
}
}
}
From the documentation for the accepted() signal of TextField:
This signal is emitted when the Return or Enter key is pressed.

QML: move to next control in form

How can I move focus from one control to next one inside QML form?
By default it works with Tab button but I need to change it to Enter.
All the control are ordered with Gridlayout with 2 columns.
I've defined a new component, TextFieldMoveOnReturn.qml
import QtQuick 2.0
import QtQuick.Controls 1.1
TextField {
Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus()
}
If you use this one instead of TextField, you get the required behaviour
edit a better solution: define a new component GridLayoutNextOnReturn.qml
import QtQuick 2.0
import QtQuick.Layouts 1.1
GridLayout {
Keys.onReturnPressed: {
for (var i = 0; i < children.length; ++i)
if (children[i].focus) {
children[i].nextItemInFocusChain().forceActiveFocus()
break
}
}
}
and use normal TextField inside - works like a charm
In order to make it more robust and flexible, you should make same behaviour
for Tab and Enter/Return keys.
Handle keyPressed event and use KeyNavigation.tab instead of nextItemInFocusChain to focus next element as follow:
import QtQuick 2.12
import QtQuick.Controls 1.12
Column {
TextField {
id: field1
KeyNavigation.tab: field2
activeFocusOnTab: true
Keys.onReturnPressed: KeyNavigation.tab.forceActiveFocus();
}
TextField {
id: field2
KeyNavigation.tab: field3
activeFocusOnTab: true
Keys.onReturnPressed: KeyNavigation.tab.forceActiveFocus();
}
TextField {
id: field3
KeyNavigation.tab: field1
activeFocusOnTab: true
Keys.onReturnPressed: KeyNavigation.tab.forceActiveFocus();
}
}
So you controlled order of focus and users can use both tab and return keys interchangeably which results better UX.
Whenever you want to change order, just change KeyNavigation.tab values :)
Note: I extremely suggest you to avoid using nextItemInFocusChain because of future changes and flexibility
You can use onEditingFinished:
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
Rectangle {
width: 400
height: 400
GridLayout {
anchors.fill: parent
columns: 2
Label {
text: "Name"
}
TextField {
onEditingFinished: addressEdit.focus = true
}
Label {
text: "Address"
}
TextField {
id: addressEdit
}
}
}
Use Keys.onReturnPressed and forceActiveFocus()
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
Rectangle {
width: 400
height: 400
GridLayout {
anchors.fill: parent
columns: 2
Label {
text: "Name"
}
TextField {
Keys.onReturnPressed: organizationEdit.forceActiveFocus()
}
Label {
text: "Organization"
}
TextField {
id: organizationEdit
Keys.onReturnPressed: addressEdit.forceActiveFocus()
}
Label {
text: "Address"
}
TextField {
id: addressEdit
}
}
}

Resources