Quelle disposition utiliser pour un clavier visuel ?

Le développement de CiviKey avançant il est temps de se poser la question de la disposition des touches. Historiquement l’interface a toujours suivi les dispositions des claviers physiques : un clavier Azerty et un clavier Qwerty comportant toutes les touches d’un clavier normal, avec pavé numérique et touches de fonctions.

Pourtant ce n’est pas forcément la solution la plus ergonomique, aussi bien pour un utilisateur valide que pour un utilisateur handicapé. Heureusement de nombreuses personnes se sont posés la question avant nous, comme Maxime Baas, Yohan Guerrier et Christophe Kolski qui ont publié deux articles traitant de la question, intitulés « Etude comparative entre clavier virtuel de type AZERTY et K-Hermès, destinés à des utilisateurs souffrant d’une Infirmité Motrice Cérébrale » et « Système de saisie de texte visant à réduire l’effort des utilisateurs à handicap moteur ». Le premier étant une comparaison entre deux claviers, un AZERTY (Clavicom NG) et le clavier K-Hermès dont les touches possèdent trois lettres et sont disposées en fonction des probabilités avec les lettres suivantes (par exemple dans la langue française le ‘q’ est souvent suivi du ‘u’, donc la touche contenant ‘u’ sera proche de la touche contenant ‘q’). Le second étant une recherche de disposition idéale visant à réduire l’effort aussi bien physique que psychique des utilisateurs du clavier.

Nous avons donc essayé de tirer de ces articles une définition du « clavier idéal », voici les points qu’il doit respecter :

  • Le nombre de touches du clavier doit être minimum.
  • Les touches retour et espaces étant les touches les plus utilisées sur un clavier, elles doivent être très facilement et rapidement accessibles, voire même multiples.
  • La prédiction de mot / de lettre, doit être intégré au clavier nativement et ne doit pas être une surcouche complexe à utiliser. Elle doit faire partie du comportement normal du clavier.
  • La disposition des touches ne doit pas forcement être calculée en fonction des touches les plus probables mais doit plutôt pouvoir être mémorisée très facilement. (Limiter le nombre de touches participe à ce point.)
  • Toujours dans l’optique de réduire le nombre de touches, il faut utiliser toutes les possibilités des périphériques d’entrée afin de supprimer les touches de changement de mode : le clic gauche peut envoyer une lettre en minuscule tandis que le clic droit envoi une majuscule, d’autre modes peuvent être activés par un clic long gauche et droit par exemple.
  • La grille n’est pas la meilleure solution : dans une grille, chaque touche est entourée au maximum de quatre autre touches… un placement en quinconce permet d’entourer chaque touche de six touches au maximum.

Voici donc la nouvelle disposition, disposée en tenant compte des points cités ci-dessus :

Cette disposition est évidemment amenée à évoluer mais elle représente le standard de ce que doit être un clavier visuel qui doit être plus facile à utiliser qu’une reproduction directe d’un clavier physique.

Liens entre structure et layout

Les travaux sur la version 2.5.0 du Core, et plus précisément le développement de la nouvelle interface graphique de CiviKey nous ont permis de soulever une nouvelle problématique qui a entrainé l’évolution de la structure même de ce que sont un clavier et son affichage (ou layout).

Un petit rappel ne fait pas de mal

Les classes qui définissent la structure des objets d’un contexte CiviKey sont organisées en deux sections parallèles :

  • la structure de « ce qu’est un clavier », au sens des données liées au comportement et à la nature des objets (propriété Enabled et commandes associées à l’appui d’une touche par exemple)
  • et la façon dont ces claviers doivent être affichés : les Layouts (propriété Visible et positionnement à l’écran par exemple).

 

D’abord, de nouveaux noms

Lors de l’évolution décrite ci-après, on s’est rapidement rendu compte que les noms des classes n’étaient pas suffisamment explicites : ils avaient déjà évolué une première fois pour perdre le préfixe CVK (ICVKContext est devenu IContext), et l’ajout des interfaces « Current » (voir ci-dessous) a fait déborder le vase (avec un horrible ICurrentActualKeyLayoutCurrent…). Olivier a donc décidé de renommer certaines interfaces pour plus de clarté : IKeyboardZone devient IZone (parce qu’après tout, lorsqu’on travaille dans CiviKey on sait bien qu’une zone est une zone de clavier) ; IActualKey devient IKeyMode (car « actual » étant un faux-ami en anglais, certains ne comprenait pas le sens « Réel » de ce nom, IKeyMode est plus clair car cet objet représente simplement une touche - ou Key - dans un Mode donné). Du côté des layouts certains noms ont changé aussi en respectant une parfaite cohérence avec la section structurelle. L’intérêt de ce re-nommage est double :

  • les noms des objets sont « alignés » sur leur « structure » et il est beaucoup plus facile de s’y retrouver.
  • cela permet aussi de séparer les objets Layout des objets Structure dans la liste des classes dans Visual Studio : les Layouts, commençant tous par « ILayout » sont regroupés et non plus éparpillés parmi les autre classes/interfaces.

 

Voici donc la nouvelle organisation des interfaces du modèle :

Avant Maintenant
  • IContext
    • IKeyboard
      • IKeyboardZone
        • IKey
          • IActualKey
      • IKeyboardLayout
        • IKeyboardZoneLayout
          • IKeyLayout
            • IActualKeyLayout
  • IContext
    • IKeyboard
      • IZone
        • IKey
          • IKeyMode
      • ILayout
        • ILayoutZone
          • ILayoutKey
            • ILayoutKeyMode

Mais où est le problème ?

Avant tout, si cela n’est pas déjà fait, il vous faut commencer par lire l’article d’Olivier sur les modes et les fallbacks calculés lorsqu’un IActualKey n’existe pas dans le mode courant.

Désormais armés pour comprendre les modes ainsi que les fallbacks, voici le souci lié à l’ancienne conception de ces classes:

Les relations qui lient les IXXX aux ILayoutXXX sont de type 1:1, et cela est très bien … sauf pour les IKeyMode vs. IKeyModeLayout. En effet cela oblige chaque IKeyMode à « posséder » son ILayoutKeyMode, donc pour deux IKeyMode il faut deux ILayoutKeyMode distincts, ce qui impose donc redéfinir les valeurs des propriétés de ces ILayoutKeyMode, or dans la plupart des cas (99% je pense) les propriétés de layout (largeur, hauteur, position) ne changent pas entre deux IKeyMode différents (ce qui change, ce sont le comportement ou le label). Cela entraine une copie des données, et du coup une surcharge, aussi bien pour le système que pour l’utilisateur lors de la modification de l’affichage d’un clavier (pour déplacer une touche, il faudrait modifier la position de chacun de ses IKeyMode via chaque ILayoutKeyMode).

La solution trouvée à ce problème est assez simple, il suffit d’appliquer le même principe de fallback (la façon dont on retrouve le « meilleur » IKeyMode pour un mode donné) aux ILayoutKeyMode que celui applicable aux IKeyMode. L’impact est qu’il n’y a plus de lien direct entre un IKeyMode et un ILayoutKeyMode. Par contre il est toujours possible via tout IKey de retrouver le ILayoutKey courant, et sur cet ILayoutKey de trouver le « meilleur » ILayoutKeyMode calculé en fonction du mode courant (et non en fonction de l’IKeyMode courant).

Afin de faciliter la programmation, deux nouvelles interfaces ont étés introduites dans le modèle : un IKeyModeCurrent étend IKeyMode, et un ILayoutKeyModeCurrent étend ILayoutKeyMode. Ces deux interfaces ne peuvent être obtenues que via les différentes propriétés « Current » et ne font qu’ajouter une propriété « IsFallBack » qui est vrai si l’objet ne correspond pas exactement au mode courant (mais est juste le « meilleur » objet calculé pour le mode courant).

Pour plus de détails vous trouverez ici le diagramme de classes des objets du Contexte (aussi disponible dans le dossier Context/Model du projet CK.Context de la solution CK.Core).

Le fallback est plus simple à comprendre car il s’applique de façon indépendante aux touches et à leurs layouts. L’implémentation bénéficie également de cette simplification : les objets (internes au CK.Context) qui implémentent IKeyMode et ILayoutKeyMode utilisent exactement le même mécanisme de gestion des fallbacks (qui, à l’occasion, a été optimisé).