Le graphe de contrôle logique - gestion des événements d'entrée - input event handling

Les utilisateurs inter-agissent avec le graphe de scéne en nourrissant le serveur d'affichage avec des événements, à partir d'une très large variété de périphériques d'entrées physiques. A la différence de la plupart des GUIs, nous avons choisi une approche extensible et avec "peu d'impact" pour distribuer les événements à leurs destinataires respectifs.

Périphériques d'entrées et les types d'événements

C'est une tâche difficile de concevoir une interface utilisateur qui fonctionne non seulement avec tous les périphériques d'entrées existants mais aussi pour des périphériques qui n'ont pas encore été conçus. Pour cette raison, et parce que l'environnement concret peut être très différent pour deux utilisateurs, les périphériques physiques et leurs données d'entrée pour les périphériques logiques sont cartographiés ainsi que les ensembles de données élementaires. Les données d'entrée sont classées suivants certains attributs (types) comme :

Table 1-1. Attributs de périphériques

nomtype
Rapporteur (telltale)Ensemble de bits
Touche (key)Bascule (Toggle)
Bouton (button)Bascule (Toggle)
Situation (positional)Position
Evaluation (valuation)Réel
Chaque périphérique logique maintenant possède un certain nombre de ces attributs qui sont la seule signification pour Berlin pour les décrire. Par exemple, la souris et le claviers seraient décrits ainsi :

Table 1-2. Périphériques logiques

Périphériquenomtypedescription
Clavier
0Touche (key)Bascule (Toggle)Le symbole de la touche (en unicode ?)
0Rapporteur (telltale)Ensemble de bitsEnsemble de modifieurs courrant
Souris
1Situation (positional)PositionL'endroit courant
1Bouton Bascule Le bouton activé
1Rapporteur (telltale)Ensemble de bits Boutons préssés
Le gestionnaire d'événements de Berlin EventManager utilisera de telle description pour créer des types d'évenement adéquat pour porter la donnée associée à chaque attribut. Un Event est rien de plus qu'une liste de paires périphérique/attribut où un attribut possède un discriminateur (type) et une valeur. Ce principe bas" sur la composition permet aussi aux périphériques d'être couplé. Par exemple, les événements de souris déclanchent traditionnellement différentes commandes dépendant du fait qu'une touche est pressé ou non. Ceci peut être réalisé simplement en synthétisant les événements appropriés avec les données suivantes :
        Input::Position position = ...;
        Input::Toggle button = ...;
        Input::Bitset keymodifiers = ...;
        Input::Event event;
        event.length(3);
        event[0].device = 0;
        event[0].attr.location(position);
        event[1].device = 0;
        event[1].attr.selection(button);
        event[2].device = 1;
        event[2].attr.state(keymodifiers);
        ...
        

Contrôleurs, focus et sélection (controllers, focus et picking)

Pour qu'un événement ait un effet sur une application, ils doivent être "distribués" de la queue d'événements où ils sont à l'origine, et être "reçus" par des objets appropriés dans le graphe de scène. De tels objets sont appelés Controllers.

Décorateurs (decorators) qui consomment des événements

Les Controllers sont implémentés dans des illustrations "décoratrices" invisibles. Ce sont les parents des illustrations que vous utiliserez naturellement pour recevoir des événements. Donc, dans le cas d'un bouton, par exemple, l'"image" d'un rectangle arrondi avec un libellé à l'intérieur est un enfant d'un contrôleur invisible qui recevra et traitera les clics de souris. Le bord du bouton reflète simplement l'état du contrôleur. Cela a l'avantage que n'importe quelle illustration peut devenir un recipient "actif" d'événements simplement en ajoutant une couche sur un contrôle adéquat.

focus

Lorsqu'un contrôleur reçoit un événement, on dit qu'il tient le focus pour le périphérique dont l'événement est originaire. Il y a deux manières fondamentallement différentes pour changer le focus pour un périphérique donnée. Les événements de position sont - à moins qu'un d'un périphérique soit actif - distribués au contrôleur le plus elevé coupant la position de l'événement. Le choix de ce controleur est fait par une traverse de sélection. Pour des périphériques autres que de position, un contrôleur peut demander explicitement le focus.

Graphe de contrôle logique (logical control graph)

Si vous reétudiez la partie sur le graphe de scène et que vous vous concentrez uniquement sur les contrôleurs, vous verrez qu'ils forment une sorte de "sous-graphe" dans la scène dont on fait référence par le terme graphe de contrôle logique, puisque c'est un ensemble de noeuds dans lequel la plupart des applications y mettent leur logique. Les méthodes nécessaires pour construire le graphe de contrôle sont :

	interface Controller
	{
          void appendController(in Controller c);
          void prependController(in Controller c);
          void insertController(in Controller c);
          void replaceController(in Controller c);
          void removeController();
	};
	
Notez que le graphe de contrôle n'est pas nécessairement isomorphe au graphe de scène bien que nous pouvions nous y attendre de façon intuitive. Puisque le graphe de contrôle définit (pratiquement) l'ordre traversé pour la navigation du focus non proprositionnel, c'est le comportement désiré qui définit finalement la topologie de ce graphe.

Sélection (picking)

Pour des événements de positions, le controleur cible doit être déterminé - du moins si aucun périphérique n'est actif - en comparant la position de l'événement avec l'état réel de l'écran des illustrations. Cet algorithme de recherche est appellé sélection (picking) et il est implémenté par l'intermédiaire d'un PickTraversal qui traverse le graphe de scène. Pendant qu'il fait cela, il maintient une pile grantissante et rétrecissante d'informations représentant l'état courant de la traverse. Cette pile représente un "parcours" ou un "chemin" de l'illustration courante traversée. Nous avons besoin de créer une "photographie" de ce parcours sur l'illustration touchée. Ce qui est fait par l'appel de hit sur le PickTraversal, aboutissant à la création d'une image.

Imaginez la souris qui clique sur le polygone rouge. Cela aura pour conséquence d'appeler la méthode pick de TransformFigure qui ressemble à :
        void TransformFigure::pick(PickTraversal_ptr traversal)
        {
          if (ext-valid && traversal-intersectsRegion(ext))
            traversal-hit();
        }
        
La parcours du Traversal, lorsque la prise (hit) survient, contient les entrées suivantes : Pour chaque position, la trace contient les informations suivantes :

Table 1-3. Information sur la pile à chaque position dans une traverse

Illustration (Graphic)Le noeud actuel dans un graphe de scène
Etiquette (Tag)un identifieur pour le bord du parent
Région (Region)allocation, dans le coordonnées locales
Transformation (Transform)La transformation à partir des systèmes de coordonnées globales ou locales
Puisque après la fin de la traversé, la pile sera vide, le Picktraversal a besoin de créer une image de lui-même, qui pourra être utilisé plus tard lors de la livraison de l'événement. La pile du controleur, extraite de la trace, est Elle est utilisée pour mettre à jour le focus, c'est-à-dire pour appeller (dans l'ordre approprié) toutes les méthodes receiveFocus des controlleurs. A partir de cette méthode, les controlleurs peuvent manipuler des données globales tels que des pointeurs d'images, des menus dépendant du contexte, etc. En particulier, un Controler peut vouloir installer un Event::Filter par lequel un événement doit traverser avant que le controleur supérieur (l'Editor dans ce cas) ne puisse le manipuler.
        CORBA::Boolean ControllerImpl::receiveFocus(Focus_ptr f)
        {
          set(Telltale::active);
          f-setPointer(myPointer);
          return true;
        }
        
Finallement, si tou sles filtres laisse passer l'évenement, la méthode de manipulation de l'Editor sera appelée. Il verra la trace ainsi : Un itérateur spéciale lui permet d'accéder aux illustrations qui, à l'intérieur de l'éditeur, étaient à l'intersection avec le périphérique.

Périphériques de navigation non positionnés

Vous pouvez faire naviguer le focus à travers le graphe de contrôle par les méthodes suivantes :
        interface Controller
        {
          boolean requestFocus(in Controller c, in Event::Device d);
          boolean receiveFocus(in Focus f);
          void loseFocus(in Focus f);

          boolean firstFocus(in Event::Device d);
          boolean lastFocus(in Event::Device d);
          boolean nextFocus(in Event::Device d);
          boolean prevFocus(in Event::Device d);
        };