Search code examples
qtfocusqmlstackview

Qt QML Focus an Item (TextField) when showing a StackView Page


I want to enable TextField focus when QML file is loaded. But, it is not working. After loading TestUi.qml file I put some button and its onClick() method I did _recipientView.focus = true_, it works fine. The problem is that default focus is not enabled when view is loaded first time.

TestUi.qml

import QtQuick 2.0
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.0


Page {

    function init() {
        recipientView.focus = true;
    }

    TextField {
       id: recipientView
        Layout.fillWidth: true
        font.pixelSize: 18
        inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhEmailCharactersOnly
        focus: true
        placeholderText: "Email"
    
    }
}

main.qml

onComposeBtnClicked: {
    rootStackView.push(test)
    test.init()
}

TestUi {
    id: test
    visible: false
}

Solution

  • Edit

    The Page component already acts as a FocusScope, so only forcing the active focus is necessary. Thanks for the comments.

    StackView {
        id: stackView
        initialItem: firstPage
    
        // Ensures the focus changes to your page whenever
        // you show a different page
        onCurrentItemChanged: {
            currentItem.forceActiveFocus()
        }
    }
    
    Page {
        id: firstPage
        visible: false
    
        TextField {
            // Explicitly set the focus where needed
            focus: true
        }
    }
    

    Original answer

    This is simply because you are pushing TestUi into your stack with rootStackView.push(test). When you do so, the focus is reset. This is typically handled with a QFocusScope, which role is to remember the focused Item, and give the focus back to it when the QFocusScope regains focus.

    In your case, adding a QFocusScope to your base page would enable restoring the focus correctly when the page is shown:

    StackView {
        id: stackView
        initialItem: firstPage
        onCurrentItemChanged: {
            currentItem.forceActiveFocus()
        }
    }
    
    Page {
        id: firstPage
        visible: false
        onFocusChanged: {
            scope.focus = true
        }
        FocusScope {
            id: scope
            TextField {
                focus: true
                // ...
            }
        }
    }
    

    You can then use your page handler onVisibleChanged if you want to reset to focus when the user comes back to it (after a pop for instance), instead of memorizing it where the focus is. But in that case the FocusScope might be overkill.

    For information, you can also use the StackView property initialItem in order to set the first page.

    It is also kind of unrelated, but prefer importing the most recent version of the QtQuick components available. QtQuick version will be 2.12 for Qt 5.12. A bit less trivial for QtQtcuik.Controls version, but they are getting in line with that versioning scheme.