Search code examples
keyboardqmlvirtual

HowTo write a QML-Keyboard with return value


I'm using lot's of TextInputs within ListViews in my QML-Application. To modify the value I'm providing a virtual QML-Keyboard, that also contains a TextInput.

When I click on a TextInput in the ListView, the TextInput within the QML-Keyboard get's focus and the user can start editing. When finished the text should be sent to the TextInput within the ListView.

The problem I have is, that I don't know how to copy the text of the keyboard's TextInput to the ListView's-TextInput because the focus is lost when starting my virtual keyboard.


Solution

  • Each delegate item in a ListView is univocally identified by its index attached property. The first delegate in the list has index 0, then 1 and so on.

    A ListView has a property currentIndex referring to the currently selected item delegate. When the currentIndex is set to a specific delegate also the currentItem property is set to the corresponding delegate object.

    Given these properties, you can exploit them to obtain the desired behaviour. When you select a TextInput to edit it, you can set the currentIndex of the list to the index of the TextInput delegate. In this way also currentItem is set and it can later be used to refer the delegate (and the TextInput inside) when the editing in the virtual keyboard is finished.

    Here is an example that better explains my point:

    import QtQuick 2.3
    import QtQuick.Window 2.0
    import QtQuick.Layouts 1.1
    
    Window {
        width: 200
        height: 300
    
        visible: true
    
        Component {
            id: myDelegate
            Rectangle {
                width: myList.width; height: 20
                color: myList.currentIndex === index ? "lightgreen" : "lightgray"  // distinguish the selecte delegate 
                onFocusChanged: textInput.forceActiveFocus()
                property alias textInput: textInput     // alias the textInput   // (1)
    
                TextInput  {
                    id: textInput
                    anchors.fill: parent
    
                    onCursorVisibleChanged: {
                        if(cursorVisible)
                        {
                            myList.currentIndex = index   // (2)
                            keyboardInput.text = textInput.getText(0, textInput.text.length) // (3)
                            keyboardInput.forceActiveFocus()
                        }
                    }
                }
            }
        }
    
        ColumnLayout {
            anchors.fill: parent
    
            ListView {
                id: myList
                model: 5
                delegate: myDelegate
                spacing: 10
                Layout.fillWidth: true
                Layout.fillHeight: true
            }
    
            Rectangle {
                Layout.alignment: Qt.AlignBottom
                color: "steelblue"
                Layout.preferredWidth: parent.width
                Layout.preferredHeight: 40
                TextInput  {
                    anchors.centerIn: parent          // simulate the keyboard
                    id: keyboardInput
                    width: parent.width
                    font.pixelSize: parent.height
                    onEditingFinished: {
                        myList.currentItem.textInput.text = getText(0, text.length)   //(4)
                    }
                }
            }
        }
    }
    

    This is a simplified example in which your virtual keyboard is substituted by a blue TextInput with id keyboardInput. Each time a TextInput in the list is focused, the focus is passed to keyboardInput for editing text.

    First of all the TextInput in the delegate is aliased (1) to be accessible from outside the delegate. When a TextInput in the list get focused for editing (in my case I consider the CursorVisibleChanged event), the list currentIndex is set to the index of the delegate (2) and also the current text in the TextInput is copied inside keyboardInput (3). When the editing is finished, the text inside keyboardInput is copied back to the currently selected TextInput via currentitem (4).

    This approach applies even with more than one ListView: simply store the current list in a variable at an higher scope and refer that variable in (4) to set the text in the correct delegate.