xulfr.org

7.7 Commandes

Écrit par Neil Deakin. Traduit par Laurent Jouanneau (15/11/2004).
Page originale : http://www.xulplanet.com/tutorials/xultu/commands.html xulplanet.com

Une commande est une opération qui peut être invoquée.

Les éléments de commande

L'élément command est utilisé pour créer des commandes qui pourront être utilisées pour exécuter des opérations. Vous n'avez pas besoin d'utiliser les commandes si vous avez juste à appeler un script pour manipuler des choses. Cependant, une commande a l'avantage qu'elle peut être désactivée automatiquement quand c'est nécessaire, et peut être invoquée de l'extérieur sans avoir besoin de savoir les détails de son implémentation. Les commandes fournissent un moyen pour séparer de façon abstraite les opérations et le code. Elles deviennent très utiles pour les grosses applications.

Par exemple, pour implémenter les commandes de menus du presse-papiers, couper, copier et coller, vous pouvez utiliser les commandes. Si vous ne les utilisiez pas, vous devriez trouver quel champ a le focus, ensuite s'assurer que l'opération est valable pour cet élément. De plus, les commandes de menus devraient être activées ou désactivées selon si l'élément cible a du texte sélectionné ou pas, et pour les opérations de collage, si il y a quelque chose de valable dans le presse-papiers, à coller. Comme vous pouvez le voir, cela devient compliqué. En utilisant les commandes, votre travail est simplifié.

Vous pouvez utiliser une commande pour n'importe quelle opération. Mozilla les utilise la plupart du temps pour les menus. De plus, les champs de saisie de texte et autres composants graphiques ont un nombre de commandes qu'ils supportent nativement, que vous pouvez donc invoquer. Vous devriez les utiliser quand les opérations dépendent de l'élément sélectionné.

Une commande est identifiée par son attribut id. Mozilla utilise une convention : les id de commandes commencent par cmd_. Vous voudrez probablement utiliser le même identifiant que celui d'une commande déjà utilisée, cependant, pour vos propres commandes, vous pouvez utiliser n'importe quel id de commande souhaité. Pour éviter les conflits, il est préférable d'inclure le nom de l'application dans l'id de la commande. Un moyen simple d'utilisation des commandes est montré ci-après :

Exemple 7.7.1 : Source Voir.

<command id="cmd_openhelp" oncommand="alert('Help!');"/>

<button label="Help" command="cmd_openhelp"/>

Dans cet exemple, au lieu de placer l'attribut oncommand sur l'élément button, nous le plaçons sur un élément command. Les deux sont alors liés en utilisant l'attribut command, qui a la valeur de l'id de la commande. Ainsi quand le bouton est pressé, la commande est invoquée.

Il y a deux avantages en utilisant cette approche. Premièrement, cela déplace toutes les opérations dans des commandes, qui peuvent être toutes regroupées ensemble dans une seule section de fichier XUL. Cela signifie que ce code est au même endroit, et n'est pas éparpillé dans tout le code de l'interface utilisateur. Le deuxième avantage est que différents boutons et autres éléments de l'interface graphique peuvent être rattachés à une même commande. Par exemple, vous pouvez avoir un item de menu, un bouton d'une barre de boutons et un raccourci clavier pour effectuer la même opération. Plutôt que de répéter le code trois fois, vous pouvez rattacher ces trois éléments à la même commande. Normalement, vous rattacherez seulement les éléments qui enverront un événement de commande.

Si vous spécifiez l'attribut disabled sur une commande, elle sera désactivée et ne pourra pas être invoquée. En plus de cela, tous les boutons et les items de menus qui lui sont rattachés seront désactivés automatiquement. Si vous réactivez la commande, les boutons deviendront actifs de nouveau.

Exemple 7.7.2 : Source Voir

<command id="cmd_openhelp" oncommand="alert('Aide');"/>

<button label="Aide" command="cmd_openhelp"/>
<button label="Plus d'aide" command="cmd_openhelp"/>

<button label="Désactiver"
        oncommand="document.getElementById('cmd_openhelp').setAttribute('disabled','true');"/>

<button label="Activer"
        oncommand="document.getElementById('cmd_openhelp').removeAttribute('disabled');"/>

Dans cet exemple, les deux boutons utilisent la même commande. Quand le bouton "désactiver" est pressé, la commande est désactivée en mettant son attribut disabled, et les deux boutons seront aussi désactivés.

Il est usuel de mettre un groupe de commandes à l'intérieur d'un élément commandset, prés du début du fichier XUL, comme ce qui suit :

<commandset>
  <command id="cmd_open" oncommand="alert('Open!');"/>
  <command id="cmd_help" oncommand="alert('Help!');"/>
</commandset>

Une commande est invoquée quand l'utilisateur active le bouton ou les autres éléments rattachés à la commande. Vous pouvez aussi invoquer une commande en appelant la méthode doCommand, que ce soit de l'élément command ou d'un élément rattaché à la commande comme un bouton.

Le répartiteur de commandes

Vous pouvez aussi utiliser les commandes sans utiliser l'élément command, ou, au moins, sans ajouter un attribut oncommand sur la commande. Dans ce cas, la commande n'invoquera pas un script directement, mais recherchera à la place un élément ou une fonction qui traitera la commande. Cette fonction peut être séparée du XUL lui-même, et peut être embarquée par un élément graphique en interne. Afin de trouver ce qui traitera la commande, XUL utilise un objet appelé répartiteur de commande (NdT : command dispatcher). Cet objet localise le gestionnaire d'une commande. Le gestionnaire d'une commande est appelé contrôleur. Ainsi, quand une commande est invoquée, le répartiteur de commande localise un contrôleur qui traite la commande. Vous pouvez déduire que l'élément command est un type de contrôleur pour une commande.

Le répartiteur de commandes localise un contrôleur en regardant l'élément sélectionné pour voir si il a un contrôleur qui gère la commande. Les éléments XUL ont une propriété controllers qui est utilisée pour la vérification. Vous pouvez l'utiliser pour ajouter vos propres contrôleurs. Vous pourriez l'utiliser pour avoir une boîte de liste qui répond aux opérations de couper, copier et coller. Un exemple sera fourni plus tard. Par défaut, seules les boîtes de saisie (textbox) ont un contrôleur fonctionnel. Ce contrôleur gère aussi bien les opérations de presse-papiers, sélection, défaire et refaire que les opérations d'édition. Notez qu'un élément peut avoir plusieurs contrôleurs, qui seront alors tous pris en compte.

Si l'élément courant sélectionné n'a pas le contrôleur attendu, la fenêtre sera alors vérifié. L'élément window a aussi une propriété controllers que vous pouvez modifier comme bon vous semble. Si le focus est à l'intérieur d'une frame, chaque frame parente est également vérifiée. Cela signifie que les commandes fonctionneront même si le focus est à l'intérieur d'une frame. Cela fonctionne bien pour un navigateur : les commandes d'édition invoquées à partir du menu principal fonctionneront à l'intérieur de la zone de contenu. Notez que HTML a aussi un système de commandes et de contrôleur, bien que vous ne pouvez pas l'utiliser sur des pages Web non privilégiées. Mais vous pouvez l'utiliser, par exemple, dans une extension du navigateur. Si la fenêtre ne fournit pas un contrôleur capable de gérer la commande, rien ne se passera.

Vous pouvez récupérer le répartiteur de commande en utilisant la propriété commandDispatcher de l'objet document ou pouvez le récupérer à partir des contrôleurs listés dans un élément ou la fenêtre. Le répartiteur de commande contient des méthodes pour récupérer les contrôleurs pour les commandes et pour récupérer et modifier le focus.

Vous pouvez implémenter vos propres contrôleurs pour répondre aux commandes. Vous pouvez tout aussi bien surcharger la gestion par défaut d'une commande en plaçant le contrôleur correctement. Un contrôleur doit implémenter quatre méthodes, qui sont listées ci-dessus :

supportsCommand (command)
Cette méthode doit renvoyer true si le contrôleur gère la commande. Si vous renvoyez false, la commande n'est pas gérée et le répartiteur de commande interrogera un autre contrôleur. Un contrôleur peut gérer plusieurs commandes.
isCommandEnabled (command)
Cette méthode doit renvoyer true si la commande est activée, false sinon. Les boutons correspondants seront désactivés automatiquement.
doCommand (command)
exécute la commande. C'est ici que vous mettrez le code pour gérer la commande.
onEvent (event)
Cette méthode gère un événement.

Imaginons que nous voulions implémenter une boîte de liste (listbox) qui gère la commande "effacer" (delete). Quand un utilisateur sélectionne "effacer" dans le menu, la boîte de liste efface la ligne sélectionnée. Dans ce cas, vous avez juste à attacher un contrôleur à l'élément listbox qui exécutera la méthode doCommand.

Essayez d'ouvrir l'exemple qui suit dans une fenêtre du navigateur et sélectionnez des items de la liste. Vous noterez que la commande "effacer" du menu "édition" du navigateur est activée, et que la choisir effacera la ligne. L'exemple n'est cependant pas complet. Nous devrions nous assurer que la sélection et le focus soient ajustés comme il faut après l'effacement.

Exemple : voir

<window id="controller-example" title="Exemple de contrôleur" onload="init();"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<script>
function init()
{
  var list = document.getElementById("theList");

  var listController = {
    supportsCommand : function(cmd){ return (cmd == "cmd_delete"); },
    isCommandEnabled : function(cmd){
      if (cmd == "cmd_delete") return (list.selectedItem != null);
      return false;
    },
    doCommand : function(cmd){
      list.removeItemAt(list.selectedIndex);
    },
    onEvent : function(evt){ }
  };

  list.controllers.appendController(listController);
}
</script>

<listbox id="theList">
  <listitem label="Océan"/>
  <listitem label="Desert"/>

  <listitem label="Jungle"/>
  <listitem label="Marécage"/>
</listbox>

</window>

Le contrôleur listController implémente les quatre fonctions décrites plus haut. La méthode supportsCommand renvoi true pour la commande cmd_delete, qui est le nom de la commande utilisé lorsque l'item de menu "Effacer" est sélectionnée. Pour les autres commandes, false est renvoyé puisque le contrôleur ne gère aucune autre commande. Si vous voulez gérer plusieurs commandes, indiquez les ici, étant donné que vous utiliserez souvent un seul contrôleur pour plusieurs commandes apparentées.

La méthode isCommandEnabled renvoi true si la commande est activée. Dans le cas présent, nous vérifions si il y a un item sélectionné dans la liste et renvoyons true si il y en a un. Si il n'y a pas de sélection, false est renvoyé. Si vous effacez toutes les lignes dans l'exemple, la commande "effacer" deviendra inactive. Vous aurez à cliquer sur la liste pour mettre à jour le menu dans cet exemple simple. La méthode doCommand sera appelée lorsque l'item de menu "effacer" sera sélectionné, et cela provoquera l'effacement de la ligne sélectionnée dans la liste. Rien ne se passera pour la méthode onEvent, aussi nous n'ajouterons pas de code pour celle-ci.

Nous attachons le contrôleur à l'élément listbox en appelant la méthode appendController des objets contrôleurs de la liste. L'objet controller a un certain nombre de méthodes qui peuvent être utilisées pour manipuler les contrôleurs. Par exemple, il y a aussi une méthode insertControllersAt qui insère un contrôleur dans un élément avant les autres. Cela peut être utile pour surcharger des commandes. Par exemple, le code suivant desactivera le collage du presse-papiers dans une zone de saisie.

var tboxController = {
  supportsCommand : function(cmd){ return (cmd == "cmd_paste"); },
  isCommandEnabled : function(cmd){ return false; },
  doCommand : function(cmd){ },
  onEvent : function(evt){ }
};

document.getElementById("tbox").controllers.insertControllerAt(0,tboxController);

Dans cet exemple, nous inserons le contrôleur à l'index 0, c'est à dire avant tous les autres. Le nouveau contrôleur supporte la commande 'cmd_paste' et indique qu'elle est désactivée. Le contrôleur par défaut de textbox ne sera jamais appelé parce que le répartiteur de commande trouve un contrôleur avant celui-ci, prenant en charge la commande en premier.


Dans la section suivante, nous allons voir comment mettre à jour les commandes.