Search code examples
iosqmltextfieldswipeqtquickcontrols2

How to make swipeable TextField in QML?


Here's what I've got: a SwipeDelegate in a ListView and TextField that fills this delegate. When I'm trying to swipe delegate, TextField gets focus, and I'm not able to do it. Here's what I need: to swipe TextField when I'm trying to swipe it and to pass focus only when I'm tapping on it. An example of what I need is behaviour of notes in "Reminders" application on iOS. Is it possible to change behaviour of this construction somehow to make it react on swipe like I need? Simplified code:

import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Window 2.3

Window {
    id: root
    width: 320
    height: 480
    visible: true

    ListModel {
        id: simpleModel

        ListElement {
            text: "some text"
        }

        ListElement {
            text: "another text"
        }
    }

    ListView {
        id: simpleView
        model: simpleModel
        anchors.fill: parent

        delegate: SwipeDelegate {
            width: parent.width
            contentItem: TextField {
                text: model.text
                onAccepted: model.text = text
            }
            swipe.right: Label {
                id: deleteLabel
                text: "Delete"
                color: "white"
                verticalAlignment: Label.AlignVCenter
                padding: 12
                height: parent.height
                anchors.right: parent.right

                SwipeDelegate.onClicked: simpleView.model.remove(index);

                background: Rectangle {
                    color: "tomato"
                }
            }
        }
    }
}

Solution

  • What I was going to suggest was:

    readOnly: true
    

    and then react to clicks on the SwipeDelegate itself to enable the editor:

    onClicked: {
        if (!swipe.complete) {
            contentItem.visible = true
            contentItem.forceActiveFocus()
        }
    }
    

    This doesn't work though, because it seems that TextField consumes mouse events even when it's read-only. I'm not sure if this is a bug or not; it could make sense that it blocks mouse events from going through to items beneath it.

    Anyway, you can hide the TextField to prevent it interfering with mouse events, and show some text in its place:

    import QtQuick 2.9
    import QtQuick.Controls 2.3
    import QtQuick.Window 2.3
    
    Window {
        id: root
        width: 320
        height: 480
        visible: true
    
        ListModel {
            id: simpleModel
    
            ListElement {
                text: "some text"
            }
    
            ListElement {
                text: "another text"
            }
        }
    
        ListView {
            id: simpleView
            model: simpleModel
            anchors.fill: parent
    
            delegate: SwipeDelegate {
                id: swipeDelegate
                width: parent.width
                text: model.text
    
                onClicked: {
                    if (!swipe.complete) {
                        contentItem.visible = true
                        contentItem.forceActiveFocus()
                    }
                }
    
                Text {
                    anchors.fill: contentItem
                    anchors.leftMargin: contentItem.leftPadding
                    verticalAlignment: Text.AlignVCenter
                    text: model.text
                    visible: !contentItem.visible
                }
    
                contentItem: TextField {
                    text: model.text
                    visible: false
                    onAccepted: model.text = text
                }
                swipe.right: Label {
                    id: deleteLabel
                    text: "Delete"
                    color: "white"
                    verticalAlignment: Label.AlignVCenter
                    padding: 12
                    height: parent.height
                    anchors.right: parent.right
    
                    SwipeDelegate.onClicked: simpleView.model.remove(index);
    
                    background: Rectangle {
                        color: "tomato"
                    }
                }
            }
        }
    }
    

    I don't know what iOS does for accepting/cancelling the input, but I'm sure you can figure that part out. Respond to that event and hide the TextField and it should all just work.