Tastaturfokus in Qt Quick

Wenn eine Taste gedrückt oder losgelassen wird, wird ein Tastenereignis erzeugt und an den fokussierten Qt Quick Item gesendet. Um die Konstruktion von wiederverwendbaren Komponenten zu erleichtern und um einige der einzigartigen Fälle von flüssigen Benutzeroberflächen zu adressieren, fügen die Qt Quick Elemente eine scope-basierte Erweiterung des traditionellen Tastaturfokusmodells von Qt hinzu.

Überblick über die Tastenbehandlung

Wenn der Benutzer eine Taste drückt oder loslässt, geschieht Folgendes:

  1. Qt empfängt die Tastenaktion und erzeugt ein Tastenereignis.
  2. Wenn QQuickWindow die focus window der Anwendung ist, wird das Tastenereignis an sie geliefert.
  3. Das Tastenereignis wird von der Szene an das Item mit aktivem Fokus weitergeleitet. Wenn kein Element den aktiven Fokus hat, wird das Tastenereignis ignoriert.
  4. Wenn das QQuickItem mit aktivem Fokus das Tastenereignis annimmt, wird die Weitergabe beendet. Andernfalls wird das Ereignis an das übergeordnete Element gesendet, bis das Ereignis akzeptiert wird oder das Stammelement erreicht ist.

    Wenn der Typ Rectangle im folgenden Beispiel den aktiven Fokus hat und die Taste A gedrückt wird, wird das Ereignis nicht weiter übertragen. Wenn die Taste B gedrückt wird, wird das Ereignis an das Stammelement weitergeleitet und somit ignoriert.

    Rectangle {
        width: 100; height: 100
        focus: true
        Keys.onPressed: (event)=> {
            if (event.key == Qt.Key_A) {
                console.log('Key A was pressed');
                event.accepted = true;
            }
        }
    }
  5. Wenn das Stammelement Item erreicht wird, lautet das Tastenereignis ignored und die normale Qt-Tastenbehandlung wird fortgesetzt.

Siehe auch die Keys attached property und KeyNavigation attached property.

Abfrage des aktiven Fokus-Elements

Ob ein Item den aktiven Fokus hat oder nicht, kann über die Eigenschaft Item::activeFocus abgefragt werden. Hier haben wir zum Beispiel einen Text Typ, dessen Text dadurch bestimmt wird, ob er den aktiven Fokus hat oder nicht.

    Text {
        text: activeFocus ? "I have active focus!" : "I do not have active focus"
    }

Erlangung des Fokus und Fokusbereiche

Ein Item fordert den Fokus an, indem es die Eigenschaft focus auf true setzt.

In sehr einfachen Fällen reicht es manchmal aus, einfach die Eigenschaft focus zu setzen. Wenn wir das folgende Beispiel mit dem qml-Tool ausführen, sehen wir, dass der Typ keyHandler den aktiven Fokus hat und das Drücken der Tasten A, B oder C den Text entsprechend ändert.

Rectangle {
    color: "lightsteelblue"; width: 240; height: 25
    Text { id: myText }
    Item {
        id: keyHandler
        focus: true
        Keys.onPressed: (event)=> {
            if (event.key == Qt.Key_A)
                myText.text = 'Key A was pressed'
            else if (event.key == Qt.Key_B)
                myText.text = 'Key B was pressed'
            else if (event.key == Qt.Key_C)
                myText.text = 'Key C was pressed'
        }
    }
}

Wenn das obige Beispiel jedoch als wiederverwendbare oder importierte Komponente verwendet werden soll, ist diese einfache Verwendung der Eigenschaft focus nicht mehr ausreichend.

Zur Veranschaulichung erstellen wir zwei Instanzen unserer zuvor definierten Komponente und legen fest, dass die erste den Fokus haben soll. Die Absicht ist, dass beim Drücken der Tasten A, B oder C die erste der beiden Komponenten das Ereignis empfängt und entsprechend reagiert.

Der Code, der zwei MyWidget-Instanzen importiert und erstellt:

//Window code that imports MyWidget
Rectangle {
    id: window
    color: "white"; width: 240; height: 150

    Column {
        anchors.centerIn: parent; spacing: 15

        MyWidget {
            focus: true             //set this MyWidget to receive the focus
            color: "lightblue"
        }
        MyWidget {
            color: "palegreen"
        }
    }
}

Der MyWidget-Code:

Rectangle {
    id: widget
    color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
    Text { id: label; anchors.centerIn: parent}
    focus: true
    Keys.onPressed: (event)=> {
        if (event.key == Qt.Key_A)
            label.text = 'Key A was pressed'
        else if (event.key == Qt.Key_B)
            label.text = 'Key B was pressed'
        else if (event.key == Qt.Key_C)
            label.text = 'Key C was pressed'
    }
}

Wir möchten, dass das erste MyWidget Objekt den Fokus erhält, also setzen wir seine focus Eigenschaft auf true. Durch Ausführen des Codes kann es jedoch passieren, dass das zweite Widget den Fokus erhält.

Wenn man sich den Code von MyWidget und window ansieht, ist das Problem offensichtlich - es gibt drei Typen, die die Eigenschaft focus auf true setzen. Die beiden MyWidgetsetzen die Eigenschaft focus auf true und die Komponente window setzt ebenfalls den Fokus. Letztendlich kann nur ein Typ den Tastaturfokus haben, und das System muss entscheiden, welcher Typ den Fokus erhält. Da QML nicht garantiert, welches Element seine Eigenschaften zuerst initialisiert bekommt, kann es sein, dass das letzte MyWidget den anfänglichen Fokus erhält.

Dieses Problem ist auf die Sichtbarkeit zurückzuführen. Die Komponente MyWidget möchte gerne den Fokus haben, kann diesen aber nicht kontrollieren, wenn sie importiert oder wiederverwendet wird. Ebenso kann die Komponente window nicht wissen, ob ihre importierten Komponenten den Fokus anfordern.

Um dieses Problem zu lösen, führt QML ein Konzept ein, das als Fokusbereich bekannt ist. Für Qt-Benutzer ist ein Fokusbereich wie ein automatischer Fokus-Proxy. Ein Fokusbereich wird durch Deklaration des Typs FocusScope erstellt.

Im nächsten Beispiel wird ein FocusScope Typ zur Komponente hinzugefügt und das visuelle Ergebnis gezeigt.

FocusScope {

    //FocusScope needs to bind to visual properties of the Rectangle
    property alias color: rectangle.color
    x: rectangle.x; y: rectangle.y
    width: rectangle.width; height: rectangle.height

    Rectangle {
        id: rectangle
        anchors.centerIn: parent
        color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
        Text { id: label; anchors.centerIn: parent }
        focus: true
        Keys.onPressed: (event)=> {
            if (event.key == Qt.Key_A)
                label.text = 'Key A was pressed'
            else if (event.key == Qt.Key_B)
                label.text = 'Key B was pressed'
            else if (event.key == Qt.Key_C)
                label.text = 'Key C was pressed'
        }
    }
}

Konzeptuell sind Fokusbereiche recht einfach.

  • In jedem Fokusbereich kann für ein Objekt Item::focus die Eigenschaft true festgelegt werden. Wenn für mehr als ein Item die Eigenschaft focus festgelegt ist, erhält der Typ, für den zuletzt die Eigenschaft focus festgelegt wurde, den Fokus, und die anderen sind nicht festgelegt, ähnlich wie bei fehlenden Fokusbereichen.
  • Wenn ein Fokusbereich den aktiven Fokus erhält, erhält der enthaltene Typ mit der Eigenschaft focus (falls vorhanden) ebenfalls den aktiven Fokus. Wenn dieser Typ auch ein FocusScope ist, wird das Proxying-Verhalten fortgesetzt. Sowohl der Fokusbereich als auch das subfokussierte Element haben die Eigenschaft activeFocus gesetzt.

Da der Typ FocusScope kein visueller Typ ist, müssen die Eigenschaften seiner Kinder dem übergeordneten Element des Typs FocusScope mitgeteilt werden. Layouts und Positionierungstypen verwenden diese visuellen und Styling-Eigenschaften, um das Layout zu erstellen. In unserem Beispiel kann der Typ Column die beiden Widgets nicht richtig anzeigen, da FocusScope keine eigenen visuellen Eigenschaften besitzt. Die Komponente MyWidget bindet sich direkt an die Eigenschaften von rectangle, damit der Typ Column das Layout erstellen kann, das die Kinder von FocusScope enthält.

Bisher wurde im Beispiel die zweite Komponente statisch ausgewählt. Es ist nun trivial, diese Komponente so zu erweitern, dass sie anklickbar wird, und sie der ursprünglichen Anwendung hinzuzufügen. Wir setzen immer noch eines der Widgets standardmäßig als fokussiert ein. Wenn nun eines der beiden MyClickableWidgets angeklickt wird, erhält es den Fokus und das andere Widget verliert den Fokus.

Der Code, der zwei MyClickableWidget-Instanzen importiert und erstellt:

Rectangle {
    id: window

    color: "white"; width: 240; height: 150

    Column {
        anchors.centerIn: parent; spacing: 15

        MyClickableWidget {
            focus: true             //set this MyWidget to receive the focus
            color: "lightblue"
        }
        MyClickableWidget {
            color: "palegreen"
        }
    }

}

Der MyClickableWidget-Code:

FocusScope {

    id: scope

    //FocusScope needs to bind to visual properties of the children
    property alias color: rectangle.color
    x: rectangle.x; y: rectangle.y
    width: rectangle.width; height: rectangle.height

    Rectangle {
        id: rectangle
        anchors.centerIn: parent
        color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
        Text { id: label; anchors.centerIn: parent }
        focus: true
        Keys.onPressed: (event)=> {
            if (event.key == Qt.Key_A)
                label.text = 'Key A was pressed'
            else if (event.key == Qt.Key_B)
                label.text = 'Key B was pressed'
            else if (event.key == Qt.Key_C)
                label.text = 'Key C was pressed'
        }
    }
    MouseArea { anchors.fill: parent; onClicked: { scope.focus = true } }
}

Wenn ein QML Item explizit den Fokus aufgibt (indem es seine focus Eigenschaft auf false setzt, während es den aktiven Fokus hat), wählt das System nicht automatisch einen anderen Typ aus, der den Fokus erhält. Das heißt, es ist möglich, dass kein aktiver Fokus vorhanden ist.

Unter Qt Quick Beispiele - Tasteninteraktion finden Sie eine Demonstration des Verschiebens des Tastaturfokus zwischen mehreren Bereichen mit FocusScope Typen.

Erweiterte Verwendungen von Fokusbereichen

Fokusbereiche ermöglichen eine einfache Aufteilung des Fokus auf Zuweisungen. Mehrere QML-Elemente verwenden sie zu diesem Zweck.

ListViewSo ist zum Beispiel die Funktion "Fokus" selbst ein Fokusbereich. Im Allgemeinen fällt dies nicht auf, da ListView normalerweise keine manuell hinzugefügten visuellen Kinder hat. Da ListView ein Fokusbereich ist, kann es das aktuelle Listenelement fokussieren, ohne sich Gedanken darüber zu machen, wie sich dies auf den Rest der Anwendung auswirkt. Dadurch kann der Delegat des aktuellen Elements auf Tastendrücke reagieren.

Dieses konstruierte Beispiel zeigt, wie dies funktioniert. Wenn Sie die Taste Return drücken, wird der Name des aktuellen Listenelements gedruckt.

Rectangle {
    color: "lightsteelblue"; width: 100; height: 50

    ListView {
        anchors.fill: parent
        focus: true

        model: ListModel {
            ListElement { name: "Bob" }
            ListElement { name: "John" }
            ListElement { name: "Michael" }
        }

        delegate: FocusScope {
                width: childrenRect.width; height: childrenRect.height
                x:childrenRect.x; y: childrenRect.y
                TextInput {
                    focus: true
                    text: name
                    Keys.onReturnPressed: console.log(name)
                }
        }
    }
}

Das Beispiel ist zwar einfach, aber hinter den Kulissen geht eine Menge vor sich. Immer wenn sich das aktuelle Element ändert, setzt ListView die Eigenschaft Item::focus des Delegaten. Da ListView ein Fokusbereich ist, hat dies keine Auswirkungen auf den Rest der Anwendung. Wenn jedoch ListView selbst den aktiven Fokus hat, erhält der Delegat selbst den aktiven Fokus. In diesem Beispiel ist der Root-Typ des Delegaten ebenfalls ein Fokusbereich, der wiederum den aktiven Fokus an den TextInput -Typ weitergibt, der die eigentliche Arbeit der Handhabung der Taste Return ausführt.

Alle QML-Ansichtsklassen, wie z. B. PathView und GridView, verhalten sich auf ähnliche Weise, um die Tastenbehandlung in ihren jeweiligen Delegaten zu ermöglichen.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.