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.
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
| nom | type |
|---|---|
| Rapporteur (telltale) | Ensemble de bits |
| Touche (key) | Bascule (Toggle) |
| Bouton (button) | Bascule (Toggle) |
| Situation (positional) | Position |
| Evaluation (valuation) | Réel |
Table 1-2. Périphériques logiques
| Périphérique | nom | type | description |
|---|---|---|---|
| Clavier | |||
| 0 | Touche (key) | Bascule (Toggle) | Le symbole de la touche (en unicode ?) |
| 0 | Rapporteur (telltale) | Ensemble de bits | Ensemble de modifieurs courrant |
| Souris | |||
| 1 | Situation (positional) | Position | L'endroit courant |
| 1 | Bouton | Bascule | Le bouton activé |
| 1 | Rapporteur (telltale) | Ensemble de bits | Boutons préssés |
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);
...
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.
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.
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.
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.
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 |
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.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);
};