Sur cette page

Focus sur le clavier Qt Quick

Lorsqu'une touche est enfoncée ou relâchée, un événement de touche est généré et transmis à l'élément focalisé Qt Quick Item . Pour faciliter la construction de composants réutilisables et pour traiter certains des cas propres aux interfaces utilisateur fluides, les éléments de Qt Quick ajoutent une extension basée sur le champ d'application au modèle traditionnel de focalisation du clavier de Qt.

Aperçu de la gestion des touches

Lorsque l'utilisateur appuie sur une touche ou la relâche, il se produit ce qui suit :

  1. Qt reçoit l'action de la touche et génère un événement de touche.
  2. Si un site QQuickWindow est le site focus window de l'application, l'événement touche lui est transmis.
  3. L'événement clé est transmis par la scène à l'élément Item qui est au centre de l 'attention. Si aucun élément n'a le focus actif, l'événement clé est ignoré.
  4. Si le QQuickItem avec le focus actif accepte l'événement clé, la propagation s'arrête. Sinon, l'événement est envoyé au parent de l'élément jusqu'à ce que l'événement soit accepté ou que l'élément racine soit atteint.

    Si, dans l'exemple suivant, le type Rectangle a le focus actif et que la touche A est pressée, l'événement ne sera pas propagé plus loin. En appuyant sur la touche B, l'événement se propagera à l'élément racine et sera donc ignoré.

    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. Si la racine Item est atteinte, l'événement touche est ignored et la gestion normale des touches Qt se poursuit.

Voir également les pages Keys attached property et KeyNavigation attached property.

Interrogation de l'élément de focalisation actif

La propriété Item::activeFocus permet de savoir si un site Item a le focus actif ou non. Par exemple, nous avons ici un type Text dont le texte est déterminé par la présence ou non du focus actif.

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

Acquisition de la focalisation et des portées de focalisation

Un type Item demande le focus en définissant la propriété focus sur true.

Dans des cas très simples, il suffit parfois de définir la propriété focus. Si nous exécutons l'exemple suivant avec l'outil qml, nous constatons que le type keyHandler a le focus actif et que le fait d'appuyer sur les touches A, B ou C modifie le texte de manière appropriée.

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'
        }
    }
}

Cependant, si l'exemple ci-dessus devait être utilisé comme composant réutilisable ou importé, cette simple utilisation de la propriété focus n'est plus suffisante.

Pour le démontrer, nous créons deux instances de notre composant précédemment défini et nous donnons la priorité à la première. L'objectif est que lorsque les touches A, B ou C sont pressées, le premier des deux composants reçoive l'événement et réagisse en conséquence.

Le code qui importe et crée deux instances de MyWidget :

//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"
        }
    }
}

Le code de MyWidget :

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'
    }
}

Nous voulons que le premier objet MyWidget ait le focus, nous définissons donc sa propriété focus à true. Cependant, en exécutant le code, il peut arriver que le second widget reçoive le focus.

En examinant les codes MyWidget et window, le problème est évident - il y a trois types qui définissent la propriété focus sur true. Les deux MyWidgetdéfinissent la propriété focus sur true et le composant window définit également le focus. En fin de compte, un seul type peut avoir le focus clavier, et le système doit décider quel type reçoit le focus. Étant donné que QML ne garantit pas quel élément aura ses propriétés initialisées en premier, il se peut que le dernier MyWidget reçoive le focus initial.

Ce problème est dû à la visibilité. Le composant MyWidget aimerait avoir le focus, mais il ne peut pas le contrôler lorsqu'il est importé ou réutilisé. De même, le composant window n'a pas la possibilité de savoir si ses composants importés demandent le focus.

Pour résoudre ce problème, QML introduit un concept connu sous le nom de portée du focus. Pour les utilisateurs existants de Qt, une portée de focus est comme un proxy de focus automatique. Une portée de focus est créée en déclarant le type FocusScope.

Dans l'exemple suivant, un type FocusScope est ajouté au composant et le résultat visuel est montré.

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'
        }
    }
}

D'un point de vue conceptuel, les champs de focalisation sont assez simples.

  • Dans chaque champ de focalisation, un objet peut avoir Item::focus défini à true. Si plus d'un Item a la propriété focus définie, le dernier type à définir la propriété focus aura la focalisation et les autres ne seront pas définis, comme lorsqu'il n'y a pas de champ de focalisation.
  • Lorsqu'un champ de focalisation reçoit la focalisation active, le type contenu dont la propriété focus est définie (le cas échéant) reçoit également la focalisation active. Si ce type est également un FocusScope, le comportement de procuration se poursuit. L'étendue du focus et l'élément sous-focalisé auront tous deux la propriété activeFocus définie.

Notez que, puisque le type FocusScope n'est pas un type visuel, les propriétés de ses enfants doivent être exposées à l'élément parent du type FocusScope. Les mises en page et les types de positionnement utiliseront ces propriétés visuelles et de style pour créer la mise en page. Dans notre exemple, le type Column ne peut pas afficher correctement les deux widgets car le type FocusScope ne possède pas de propriétés visuelles propres. Le composant MyWidget se lie directement aux propriétés de rectangle pour permettre au type Column de créer la mise en page contenant les enfants de FocusScope.

Jusqu'à présent, l'exemple a sélectionné le deuxième composant de manière statique. Il est maintenant trivial d'étendre ce composant pour le rendre cliquable et de l'ajouter à l'application d'origine. Nous continuons à définir l'un des widgets comme étant focalisé par défaut. Désormais, le fait de cliquer sur l'un des MyClickableWidget lui donne le focus et l'autre widget perd le focus.

Le code qui importe et crée deux instances de MyClickableWidget :

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"
        }
    }

}

Le code de MyClickableWidget :

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 } }
}

Lorsqu'un QML Item renonce explicitement au focus (en définissant sa propriété focus sur false alors qu'il a le focus actif), le système ne sélectionne pas automatiquement un autre type pour recevoir le focus. En d'autres termes, il est possible qu'il n'y ait pas de focus actif.

Voir Qt Quick Examples - Key Interaction pour une démonstration du déplacement du focus clavier entre plusieurs zones à l'aide des types FocusScope.

Utilisations avancées des portées de focus

Les portées de focus permettent de répartir facilement le focus sur l'allocation. Plusieurs éléments QML l'utilisent à cet effet.

ListViewPar exemple, la balise de focus, est elle-même une balise de focus. En général, cela ne se remarque pas car ListView n'a généralement pas d'enfants visuels ajoutés manuellement. En étant une portée de focalisation, ListView peut focaliser l'élément de liste actuel sans se soucier de l'effet que cela aura sur le reste de l'application. Cela permet au délégué de l'élément courant de réagir aux pressions de touches.

L'exemple suivant montre comment cela fonctionne. L'appui sur la touche Return imprime le nom de l'élément de la liste en cours.

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)
                }
        }
    }
}

Bien que l'exemple soit simple, il se passe beaucoup de choses dans les coulisses. Chaque fois que l'élément courant change, ListView définit la propriété Item::focus du délégué. Comme ListView est une portée de focus, cela n'affecte pas le reste de l'application. Toutefois, si le site ListView a lui-même un focus actif, le délégué recevra également un focus actif. Dans cet exemple, le type racine du délégué est également une portée de focalisation, ce qui donne une focalisation active au type TextInput qui effectue le travail de gestion de la clé Return.

Toutes les classes de vues QML, telles que PathView et GridView, se comportent de la même manière pour permettre la gestion des touches dans leurs délégués respectifs.

© 2026 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.