Search code examples
qmlpyside6

In QML, how can I prevent a ComboBox from opening it's popup after gaining active focus via Keys.onEnterPressed?


In QML (6.4), I have a TextField left of a ComboBox. My users want to fill the TextField with the numpad and for ease of use, when they press the Enter key (the one on the numpad), they want to automatically focus the next input element, which is the ComboBox. I am handling that with Keys.onEnterPressed on the TextField and I use forceActiveFocus() to set the focus on the ComboBox.

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

ApplicationWindow {
    height: 400
    visibility: Window.Windowed
    visible: true
    width: 600

    RowLayout {
        anchors.fill: parent

        TextField {
            Layout.fillWidth: true
            Layout.preferredHeight: 30

            Keys.onEnterPressed: combo.forceActiveFocus()
        }
        ComboBox {
            id: combo

            Layout.fillWidth: true
            Layout.preferredHeight: 30
            model: ['option 1', 'option 2']
        }
    }
}

When I enter some text in the TextField and press Tab, the combo gets focus. When I press Enter (on the numpad) instead of Tab, the ComboBox gets focus and aditionally opens its popup (the dropdown list).

I tried to set the event.handled to true, but the situation remains identical

Keys.onEnterPressed: function (event) {
    event.handled = true
    combo.forceActiveFocus();
}

I also tried adding Keys.onTabPressed: combo.forceActiveFocus() to the TextField as well to see if forceActiveFocus was causing this behaviour, but here the popup also opens after Enter, while it does not after pressing Tab.

Then I tried combo.forceActiveFocus(Qt.TabFocusReason), also to no avail.

My intended behaviour is for Enter to behave like Tab (that is, only focus the ComboBox but not opening the popup/dropdown). Is there a way to achieve that?


Solution

  • [EDIT/REWRITE]

    After playing a bit more, I found a better solution:

        TextField {
            Keys.onReleased: function (event) {
                if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
                    event.accepted = true;
                    combo.forceActiveFocus();
                }
            }
        }
    

    The thinking behind this is when you press the enter key you get both Keys.onPressed and Keys.onReleased signals. The TextField receives the Keys.onPressed signal but, because you change active focus the ComboBox receives the Keys.onReleased signal. So the solution is to move the event handling from Keys.onPressed to Keys.onReleased. That way, the TextField will receive both Keys.onPressed and Keys.onReleased signals and you change focus during the latter signal.

    [ORIGINAL WORKAROUND SOLUTION]

    I was able to reproduce the problem in Qt6.4. I found a workaround using a Timer.

        TextField {
            Keys.onEnterPressed: nextTimer.restart()
            Keys.onReturnPressed: nextTimer.restart()
        }
        Timer {
            id: nextTimer
            interval: 200
            onTriggered: combo.forceActiveFocus()
        }
    

    If the Timer's interval is set to 100 or lower, the problem appears again. Increasing the interval to 200 or above the problem goes away. The reason why this works is it takes me about 200ms for me to press and release the enter key. The Keys.onPressed gets fired first, the input focus is changed, then, we wait until Keys.onReleased is finished before the timer gets triggered. This is why the workaround works.