nAkhmedov
nAkhmedov

Reputation: 3592

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
}

Upvotes: 5

Views: 4099

Answers (2)

S.M.Mousavi
S.M.Mousavi

Reputation: 5226

The actual/non-trick way for Qt 5.8+ is using the activated attached signal as follows:

StackView {
    id: stackView
    initialItem: firstPage
}

// As already mentioned by another answer, the Page acts like FocusScope
Page {
    id: firstPage
    visible: false
    
    // *** The Key! ***
    // Force to be active focus whenever this item is active. 
    // You need this on every loadable item with `FocusScope` behaviour.
    // So, it is a good idea to create a new component based on a `Page` 
    // that has the following signal handler
    StackView.onActivated: {
        forceActiveFocus()
    }

    TextField {
        id: txt1
        // Explicitly set the focus where needed
        focus: true
        // An example to show how you can control the focus chain
        KeyNavigation.tab: txt2
    }
    TextField {
        id: txt2
        // Previous focus item
        KeyNavigation.backtab: txt1
    }
}

Upvotes: 0

Adrien Leravat
Adrien Leravat

Reputation: 2789

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.

Upvotes: 9

Related Questions