Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

Vous n'avez pas encore de compte Developpez.com ? L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Developpez.com

Qt

Choisissez la catégorie, puis la rubrique :

Viadeo Twitter Facebook Share on Google+   
Logo Documentation Qt ·  Page d'accueil  ·  Toutes les classes  ·  Toutes les fonctions  ·  Vues d'ensemble  · 

Le focus clavier en QML

Lorsqu'une touche est appuyée ou relâchée, un évènement clavier est généré et transmis à l'élément Item QML ayant le focus. Pour faciliter la construction de composants réutilisables et pour traiter certains cas propres aux interfaces utilisateur fluides, les éléments QML ajoutent une extension basée sur les portées au modèle de focus clavier traditionnel de Qt.

Aperçu de la gestion des touches

Lorsque l'utilisateur appuie sur une touche ou la relâche, il se passe les choses suivantes :

  1. Qt reçoit l'action de la touche et génère un évènement clavier ;
  2. si le widget Qt contenant la QDeclarativeView possède le focus, l'évènement clavier lui est transmis. Sinon, la gestion standard des touches de Qt se poursuit ;
  3. l'évènement clavier est transmis par la scène à l'élément Item QML possédant le focus. Si aucun élément ne possède le focus, l'évènement clavier est ignoré et la gestion standard des touches de Qt se poursuit ;
  4. si l'élément QML ayant le focus accepte l'évènement clavier, la propagation s'arrête. Sinon, l'évènement est « remonté », il est passé à chaque parent d'élément jusqu'à ce que l'évènement soit accepté ou que l'élément racine soit atteint. Si l'élément Rectangle dans l'exemple suivant a le focus et que la touche A est appuyée, l'évènement remontera à son parent. Sinon, l'appui sur la touche B remontera l'évènement vers l'élément racine et il sera donc ignoré.
     Rectangle {
         width: 100; height: 100
         focus: true
         Keys.onPressed: {
             if (event.key == Qt.Key_A) {
                 console.log('Key A was pressed');
                 event.accepted = true;
             }
         }
     }
  5. si l'élément Item racine est atteint, l'évènement clavier est ignoré et la gestion standard des touches de Qt se poursuit.

Voir aussi les propriétés-clés attachées et propriétés KeyNavigation attachées.

Connaître l'élément possédant le focus

La propriété Item::activeFocus permet de savoir si un élément possède le focus ou non. Voici un exemple où le texte d'un élément Text est déterminé selon que l'élément possède le focus ou non.

     Text {
         text: activeFocus ? "J'ai le focus !" : "Je n'ai pas le focus"
     }

Acquérir le focus et les portées de focus

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

Pour les cas très simples, la définition de la propriété focus est suffisante. En exécutant l'exemple suivant avec le QML Viewer, on voit que l'élément keyHandler possède le focus et l'appui sur les touches A, B ou C modifie le texte selon la touche.

 Rectangle {
     color: "lightsteelblue"; width: 240; height: 25
     Text { id: myText }
     Item {
         id: keyHandler
         focus: true
         Keys.onPressed: {
             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'
         }
     }
 }

image

Par contre, lorsque l'exemple ci-dessus doit être utilisé sous forme d'un composant importé ou réutilisé, la simple utilisation de la propriété focus ne suffit plus.

Pour présenter ce cas, on crée deux instances du composant défini précédemment et on donne le focus au premier. Le but est que, lorsque l'une des touches A, B ou C est appuyée, le premier des deux composants reçoive l'évènement et réponde de façon appropriée.

Le code qui importe et crée les deux instances de MyWidget est le suivant :

 // code de la fenêtre qui importe MyWidget
 Rectangle {
     id: window
     color: "white"; width: 240; height: 150
 
     Column {
         anchors.centerIn: parent; spacing: 15
 
         MyWidget {
             focus: true             // Donne le focus à ce MyWidget
             color: "lightblue"
         }
         MyWidget {
             color: "palegreen"
         }
     }
 }

Voici le code de MyWidget :

 // code de MyWidget
 Rectangle {
     id: widget
     color: "lightsteelblue"; width: 175; height: 25; radius: 10; smooth: true
     Text { id: label; anchors.centerIn: parent}
     focus: true
     Keys.onPressed: {
         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'
     }
 }

On aimerait donner le focus au premier objet MyWidget en définissant la propriété focus à true. Cependant, en exécutant le code, on constate que c'est second widget qui reçoit le focus.

image

En observant le code de MyWidget et celui de la fenêtre window, le problème est évident - il y a trois éléments qui définissent la propriété focus à true. Les deux MyWidget définissent le focus à true et le composant fenêtre window également. En définitive, un seul élément peut posséder le focus clavier et le système doit décider quel élément recevra l'évènement. Lorsque le second MyWidget est créé, il reçoit le focus car c'est le dernier élément à définir la propriété focus à true.

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

Pour résoudre ce problème, le QML introduit un concept connu sous le nom de portée de focus (focus scope). Pour les utilisateurs de Qt, une portée de focus ressemble à un proxy automatique pour le focus. La portée est créée en déclarant l'élément FocusScope.

Dans l'exemple suivant, un élément FocusScope est ajouté au composant.

 FocusScope {
 
     // Le FocusScope doit se lier aux propriétés visuelles du 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; smooth: true
         Text { id: label; anchors.centerIn: parent }
         focus: true
         Keys.onPressed: {
             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'
         }
     }
 }

Voici le résultat visuel :

image

Conceptuellement, les portées de focus sont très simples.

  • Dans chaque portée de focus, un seul élément peut avoir la propriété Item::focus définie à true. Si plus d'un élément Item activent la propriété focus, le dernier élément à définir le focus possèdera le focus et les autres le perdront, comme lorsque qu'il n'y avait pas de portée.
  • Lorsqu'une portée de focus reçoit le focus, l'élément contenu ayant la propriété focus activée (s'il existe) reçoit aussi le focus. Si cet élément est lui-même un FocusScope, ce comportement se répète. La propriété activeFocus sera activée à la fois pour la portée de focus et le sous-élément possédant le focus.

Notez que, comme l'élément FocusScope n'est pas un élément visuel, les propriétés de ses enfants doivent être exposées à l'élément parent du FocusScope. Les dispositions et les éléments de positionnement utiliseront ces propriétés visuelles et de styles pour déterminer le positionnement. Dans notre exemple, l'élément Column ne peut afficher les deux widgets correctement car le FocusScope ne possède pas de propriétés visuelles. Le composant MyWidget se lie directement aux propriétés de rectangle pour permettre à l'élément Column de créer la disposition contenant l'enfant du FocusScope.

Jusqu'ici, l'exemple activait statiquement le focus sur le second composant. Il est maintenant trivial d'étendre ce composant pour le rendre cliquable, et de l'ajouter à l'application d'origine. On attribue toujours le focus initial à l'un des widgets. Maintenant, un clic sur l'un des MyClickableWidget lui donnera le focus et l'autre widget le perdra.

Voici le code qui importe et crée les deux instances de MyClickableWidget :

 Rectangle {
     id: window
 
     color: "white"; width: 240; height: 150
 
     Column {
         anchors.centerIn: parent; spacing: 15
 
         MyClickableWidget {
             focus: true             // donne le focus à ce MyWidget
             color: "lightblue"
         }
         MyClickableWidget {
             color: "palegreen"
         }
     }
 
 }

Voici le code de MyClickableWidget :

 FocusScope {
 
     id: scope
 
     // Le FocusScope doit se lier aux propriétés visuelles de l'enfant
     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; smooth: true
         Text { id: label; anchors.centerIn: parent }
         focus: true
         Keys.onPressed: {
             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 } }
 }

image

Lorsqu'un élément abandonne explicitement le focus (en affectant la valeur false à sa propriété focus lorsqu'il possède le focus), le système ne transmet pas automatiquement le focus à un autre élément. À ce moment, il est possible qu'aucun élément ne possède le focus.

Voir l'exemple de focus clavier pour une démonstration d'un focus clavier passant entre différentes zones en utilisant les éléments FocusScope.

Utilisation avancée des portées de focus

Les portées de focus permettent de facilement partitionner l'allocation du focus. Plusieurs éléments QML l'utilisent dans ce but.

Par exemple, ListView est elle-même une portée de focus. Généralement, cela ne se remarque pas car ListView ne possède habituellement pas d'enfants visuels ajoutés manuellement. Étant une portée de focus, la ListView peut donner le focus à l'élément courant de la liste sans se soucier des effets sur le reste de l'application. Cela permet au délégué de l'élément courant de réagir aux touches du clavier.

Cet exemple artificiel montre comment cela fonctionne. L'appui sur la touche Entrée affichera le nom de l'élément courant 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)
                 }
         }
     }
 }

image

Bien que l'exemple soit simple, beaucoup de choses se déroulent en coulisse. Chaque fois que l'élément courant change, la ListView active la propriété Item::focus du délégué. Comme le ListView est une portée de focus, cela n'affecte pas le reste de l'application. Par contre, si la ListView possède le focus, le délégué lui-même recevra le focus. Dans cet exemple, l'élément racine du délégué est aussi une portée de focus, qui donne le focus à l'élément Text qui effectue le travail de gestion de la touche Entrée.

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

Remerciements

Merci à Alexandre Laurent pour la traduction ainsi qu'à Ilya Diallo, Jonathan Courtois, Thibaut Cuvelier et Claude Leloup pour leur relecture !

Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. Qt 4.7
Copyright © 2020 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.
Vous avez déniché une erreur, une redirection cassée ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP !
Responsable bénévole de la rubrique Qt : Thibaut Cuvelier -

Partenaire : Hébergement Web