Sur cette page

Focalisation du clavier dans Qt Quick

Lorsqu’une touche est enfoncée ou relâchée, un événement de touche est généré et transmis à l’ Qt Quick, qui détient le focus Item. Afin de faciliter la création de composants réutilisables et de répondre à certains cas propres aux interfaces utilisateur fluides, les éléments Qt Quick ajoutent une extension basée sur la portée au modèle traditionnel de focus clavier de Qt XML.

Présentation de la gestion des touches

Lorsque l’utilisateur appuie sur une touche ou la relâche, le processus suivant se déroule :

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

    Si l’ Rectangle de type présenté dans l’exemple suivant a le focus actif et que la touche « A » est enfoncée, l’événement ne sera pas propagé plus loin. En appuyant sur la touche « B », l’événement se propagera jusqu’à 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 l’ Item racine est atteinte, l’événement de touche est de type « ignored » et la gestion habituelle des touches Qt se poursuit.

Voir également les méthodes ` Keys attached property ` et ` KeyNavigation attached property`.

Interrogation de l’élément ayant le focus actif

Il est possible de déterminer si un Item détient le focus actif via la propriété Item::activeFocus. Par exemple, voici un type Text dont le texte varie selon qu’il détient ou non le focus actif.

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

Obtention du focus et des portées de focus

Un élément « Item » demande le focus en définissant la propriété « focus » sur « true ».

Dans les cas très simples, il suffit parfois de définir la propriété ` focus `. Si l’on exécute l’exemple suivant avec l’outil `qml`, on constate que le type ` keyHandler ` a le focus actif et que le fait d’appuyer sur les touches ` A`, ` B` ou ` C ` modifie le texte en conséquence.

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

Widget bleu arrondi indiquant que la touche C a été enfoncée

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

Pour illustrer cela, nous créons deux instances de notre composant défini précédemment et attribuons le focus à la première. L’objectif est que, lorsque les touches A, B ou C sont enfoncées, le premier des deux composants reçoive l’événement et y réponde 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 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 sur true. Cependant, lors de l’exécution du code, il peut arriver que le deuxième widget reçoive le focus.

Deux widgets superposés, le deuxième (vert) indiquant que la touche A a été enfoncée

En examinant le code de MyWidget et de window, le problème apparaît clairement : trois types 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. Au final, 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 verra ses propriétés initialisées en premier, il se peut que le dernier MyWidget obtienne le focus initial.

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

Pour résoudre ce problème, Qml introduit un concept appelé « champ de focus ». Pour les utilisateurs actuels de Qt, un champ de focus s’apparente à un proxy de focus automatique. Un champ de focus est créé en déclarant le type FocusScope.

Dans l’exemple suivant, un type ` FocusScope ` est ajouté au composant, et le résultat visuel est présenté.

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

Deux widgets superposés, dont le premier, de couleur bleue, indique que la touche B a été enfoncée

D’un point de vue conceptuel, les « focus scopes » sont assez simples.

  • Au sein de chaque « focus scope », un objet peut avoir la propriété « Item::focus » définie sur « true ». Si plusieurs « Item » ont la propriété « focus » définie, le dernier type à avoir défini « focus » recevra le focus et les autres seront désélectionnés, comme lorsqu’il n’y a pas de « focus scope ».
  • Lorsqu’une zone de focus reçoit le focus actif, le type qu’elle contient et pour lequel la propriété « focus » est définie (le cas échéant) reçoit également le focus actif. Si ce type est également un « FocusScope », le comportement de proxy se poursuit. La zone de focus et l’élément sous-focalisé auront tous deux la propriété « activeFocus » définie.

Notez que, le type « FocusScope » n’étant pas un type visuel, les propriétés de ses enfants doivent être exposées à l’élément parent de l’ FocusScope. Les types de mise en page et 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 l’ FocusScope ne dispose pas de propriétés visuelles propres. Le composant MyWidget se lie directement aux propriétés rectangle afin de permettre au type Column de créer la mise en page contenant les enfants de FocusScope.

Jusqu’à présent, l’exemple sélectionne le deuxième composant de manière statique. Il est désormais très simple 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 actif par défaut. Désormais, 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 } }
}

Deux widgets cliquables affichant chacun la dernière touche enfoncée

Lorsqu’un « Item » QML 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 élément pour recevoir le focus. Autrement dit, il est possible qu’il n’y ait aucun focus actif à ce moment-là.

Consultez la section « Exemples d’Qt Quick s – Interaction avec le clavier » pour une démonstration du déplacement du focus clavier entre plusieurs zones à l’aide des types « FocusScope ».

Utilisations avancées des « focus scopes »

Les zones de focus permettent de partitionner facilement l’attribution du focus. Plusieurs éléments QML les utilisent à cette fin.

ListView, par exemple, est lui-même une zone de focus. En général, cela passe inaperçu car ListView ne comporte généralement pas d’enfants visuels ajoutés manuellement. En tant que zone de focus, ListView peut mettre en focus l’élément de liste actuel sans se soucier de l’impact que cela aura sur le reste de l’application. Cela permet au délégué de l’élément actuel de réagir aux frappes au clavier.

Cet exemple artificiel montre comment cela fonctionne. Appuyer sur la touche « Return » affiche le nom de l’élément actuel de la liste.

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

Liste comportant trois noms modifiables : Bob, John, Michael

Bien que l’exemple soit simple, il se passe beaucoup de choses en coulisses. Chaque fois que l’élément actif change, l’ ListView définit la propriété ` Item::focus ` du délégué. Comme ` ListView ` est une zone de focus, cela n’affecte pas le reste de l’application. Cependant, si le délégué ` ListView ` lui-même possède le focus actif, cela provoque l’attribution du focus actif au délégué lui-même. Dans cet exemple, le type racine du délégué est également une portée de focus, ce qui confère à son tour le focus actif au type ` TextInput ` qui effectue réellement le traitement de la touche ` Return `.

Toutes les classes de vue QML, telles que PathView et GridView, se comportent de manière similaire 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.