0
votes

I want my QML code to display a list of strings in text inputs and always have an empty text input at the end of the list. I have this almost working with the following code:

import QtQuick 2.11
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480

    property var values: ['test1','test2',''];

    Column {
        id: column
        anchors.fill: parent

        Repeater {
            id: repeater
            model: values
            Rectangle {
                border.width: 1
                border.color: "black"
                width: column.width
                height: childrenRect.height
                TextInput {
                    id: textField
                    text: qsTr(modelData)
                    width: column.width
                    onTextEdited: {
                        values[index] = textField.text;
                        if(values[values.length - 1] !== '') {
                            values.push('');
                            repeater.model = values;
                            textField.forceActiveFocus(); //doesn't work
                            textField.focus = true; //doesn't work
                        }
                    }
                }
            }
        }
    }
}

The only issue is that when you start typing in the empty TextInput, the app takes focus away from that TextInput after it adds the new empty one to the end of the list. I want the users to be able to smoothly add an entry to the list and then tab to the new empty input and add another one. Setting focus on the TextInput being edited to true and using the forceActiveFocus methods does not seem to work. Is there some other way I can set the focus back after the update?

2
There's something conceptually wrong here, you have a TextInput that is created from a Repeater, driven by a model (a JS array). But as soon as you modify that model, Repeater is going to destroy and recreate your delegates. And you modify that model as soon as the user types something. What is the result that you want to achieve?peppe
@peppe Your mention of the Repeater delegates being destroyed helped me realize what I was doing wrong. Thanks!Chris

2 Answers

1
votes

As you can see, the problem is the use of the list as a model, Repeater can use a list as a model but only for reading. A more efficient solution is to use ListModel since it simplifies its logic.

import QtQuick 2.11
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    ListModel {
        id: mymodel
        ListElement{
            text: "test1"
        }
        ListElement{
            text: "test2"
        }
        ListElement{
            text: ""
        }
    }
    Column {
        id: column
        anchors.fill: parent
        Repeater {
            id: repeater
            anchors.fill: parent
            model: mymodel
            Rectangle {
                width: repeater.width
                height: childrenRect.height
                border.width: 1
                border.color: "black"
                TextInput{
                    id: textinput
                    text: modelData
                    width: parent.width
                    property int my_index: index
                    onTextEdited: {
                        modelData = textinput.text
                        if(textinput.text != "" && mymodel.count == (textinput.my_index + 1)){
                            mymodel.append({"text": ""})
                        }
                    }
                }
            }
        }
    }
}
0
votes

After playing around with the focus a little more I found a way to make it work the way I want. Here is the code:

import QtQuick 2.11
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480

    property var values: ['test1','test2',''];

    Column {
        id: column
        anchors.fill: parent

        Repeater {
            id: repeater
            model: values
            Rectangle {
                border.width: 1
                border.color: "black"
                width: column.width
                height: childrenRect.height
                TextInput {
                    id: textField
                    text: qsTr(modelData)
                    width: column.width
                    onTextEdited: {
                        values[index] = textField.text;
                        if(values[values.length - 1] !== '') {
                            values.push('');
                            var c = column;
                            repeater.model = values;
                            c.children[c.children.length-3].children[0].forceActiveFocus();
                        }
                    }
                }
            }
        }
    }
}

Basically, as peppe said in his comment when the Repeater model gets updated the objects created by the Repeater are destroyed and recreated. So the way I was doing it at first where I referenced the destroyed object could never work. The approach that works gets a reference to the new TextInputs that are created by looking at the children of the parent of the Repeater.