Sous-sections

D.1 Les composants graphiques

Dans le modèle des graphes combinés, les objets graphiques sont des nœuds (objets atomiques) ou des sous-graphes (objets composites) du graphe de scène. Dans le chapitre 8 sur l'implémentation de ce modèle d'architecture logicielle avec la boîte à outils MAGGLITE, nous avons déjà présenté les classes abstraites de base pour la création de nœuds (Magglite et MaggLiteContainer), ainsi que des classes concrètes pour l'instanciation ou l'extension d'objets de base. Dans cette section annexe, nous commentons des exemples de code Java afin de préciser l'utilisation de ces composants et leur extension.

D.1.1 Instanciation et utilisation des composants graphiques

Comme nous l'avons vu dans la section 8.3.1, la boîte à outils contient plusieurs classes d'objets graphiques par défaut qui implémentent les mécanismes et comportements de base du modèle des graphes combinés. Ces classes d'objets permettent déjà la création de beaucoup d'interfaces par simple instanciation et ajout au graphe de scène.
Le code D.1.1 présente la création de trois objets graphiques dans une applicationD.1.

\begin{figure}\javafile[Exemple d'instanciation d'objets graphiques.]{inst}
\end{figure}

Le premier objet de cet exemple instancie la classe MaggLiteFoldableComponent. Comme tout composant de base de la boîte à outils, il est nécessaire de passer en argument du constructeur la forme géométrique de l'objet à créer. Il est ajouté au bureau par la méthode desk.add(mc). Cette approche de création d'objets à partir d'une forme géométrique est tout a fait adaptée à la création d'interfaces innovantes, mais aussi à l'utilisation de constructeurs d'interfaces basés sur le dessin tels que celui que nous avons proposé dans la section 8.4.2.
Le second objet créé est de la classe MaggLiteWindow, classe abstraite qui propose trois implémentations par défaut dans la boîte à outils, selon la géométrie désiré: Rectangle, Oval ou Star. Cet exemple montre l'utilisation de méthodes de manipulation de l'objet (setSize et moveTo). Les objets par défaut proposent aussi des méthodes d'accès à leur propriétés particulières (contexte graphique, état, etc.) permettant de contrôler leur apparence et leurs paramètres selon leurs capacités: setVisible, setColor, setOpacity, etc. Cet exemple utilise aussi la méthode setExternalizeManipulator qui permet de rendre disponible ou non le dispositif manipulateur de l'objet dans le configurateur graphique d'interactions. Visibles par défaut, les manipulateurs d'objets graphiques peuvent être masqués à l'initialisation de l'application. Par contre, malgré le fait que ce soit techniquement possible, la modification de cette propriété est rendue impossible dès lors que l'environnement d'ICON a été lancé, afin d'éviter la suppression de dispositifs présents dans une configuration en cours d'exécution.
Enfin, le dernier objet créé, de la classe MaggLiteSlider, est ajouté dans le composant précédent plutôt que sur le bureau.
La figure D.1 présente le résultat visuel de ce code.

Figure D.1: Objets graphiques créés par l'exemple de code D.1.1.
\includegraphics[width=250pt]{inst}

D.1.2 Nouvelles classes de composants graphiques

Il y a deux méthodes pour introduire de nouveaux objets graphiques dans la boîte à outils. Par extension des objets déjà présents, et donc héritage et surcharge de leurs capacités, ou par composition avec de nouvelles couches, et donc la création et l'utilisation de nouveaux calques.

D.1.2.1 Par héritage

Cette première méthode est celle la plus couramment utilisée dans les boîtes à outils graphiques: par extension des classes abstraites de base ou de classes plus évoluées. L'exemple D.1.2 présente une partie du code qui définit une nouvelle classe d'objets graphiques à partir de la classe abstraite de base MaggLiteContainer.

\begin{figure}\javafile[Exemple de création d'une nouvelle classe d'objets graph...
...nterfaces
\code{MaggLiteShaped} et \code{MaggLiteDraggable}.]{myml}
\end{figure}

Cette classe permet de construire des objets de forme ovale et de couleur bleue. Ces propriétés sont définies dans le constructeur (ligne 5). La classe créée étend donc la classe abstraite MaggLiteContainer, et implémente pour cela ses méthodes abstraites (lignes 13 à 20). Ce sont en particulier des méthodes d'affichage. Sur ce point, les lignes 9 et 10 montrent comment des propriétés graphiques sont définies dans le contexte de l'objet, contexte qui sera mis en place lors des opérations d'affichage de l'objet. Si certains attributs ne sont pas définis, ceux du nœud parent seront alors utilisés.
Afin de définir une forme aux objets, il faut implémenter l'interface MaggLiteShaped (lignes 22 à 31). Cela permettra aux objets d'être «reconnus» par les mécanismes et interactions génériques de la boîte à outils. Il seront alors de fait compatibles avec les opérations associées. De la même manière, cette classe implémente l'interface MaggLiteDraggable afin de pouvoir utiliser le dispositif d'interaction Drag (Glisser-Déposer) avec ces objets. D'une manière générale, les interfaces proposées dans la boîte à outils permettent la compatibilité des objets avec les IAPs qui peuvent en tirer parti (nous reviendrons sur ce principe avec l'exemple de la conception du Glisser-Déposer dans la section D.2.1).

Cet exemple minimal permet déjà d'apprécier la base de la programmation d'objets graphiques avec MAGGLITE. Par contre, partir comme nous l'avons fait des classes abstraites pour créer des objets aux propriétés plus avancées nécessite de reimplémenter des méthodes déjà présentes dans des classes de plus haut niveau (gestion des formes, du glisser-déposer, des innertools, etc...): MaggLiteComponent et ses dérivées (voir section 8.3.1). C'est pourquoi il est plus judicieux d'étendre ces classes de plus haut niveau implémentant déjà les interfaces et mécanismes souhaités, et de surcharger les méthodes correspondant aux modifications voulues (l'exemple précédent se résumerait alors à surcharger le constructeur pour définir une forme et une couleur).

Toutefois, l'extension des capacités des objets graphiques par héritage s'avère peu flexible et modulaire dans le cas fréquent où il est nécessaire d'associer de nouvelles possibilités graphiques à des interactions particulières. Prenons pour cela l'exemple du dessin. Il peut être utile, pour beaucoup d'interfaces avancées, de dessiner sur des objets de l'interface (reconnaissance de gestes, annotations, etc.). Dès lors, il faut spécifier l'interaction de dessin et les propriétés de l'objet graphique associé. Mais si l'on veut pouvoir dessiner sur plusieurs types d'objets, il faut:

  1. soit étendre leur super classe commune, afin que tous les objets héritent de cette capacité. Cette approche est possible, mais nous semble peu élégante dans notre modèle car retombant dans la vision monolithiques des boîtes à outils standards.
  2. soit étendre chacune des classes d'objets, ce qui nécessite de répéter le même code dans différentes classes, limitant alors la réutilisabilité de ce code.

D.1.2.2 Par couche (calque)

Une alternative avec MAGGLITE est d'utiliser des calques pour rajouter des couches graphiques et d'interaction aux objets. Plus flexible et modulaire, cette approche permet de composer des objets afin de leur associer de nouvelles capacités, améliorant alors la réutilisabilité des objets existants.
Pour notre exemple du dessin, il suffit d'implémenter une classe de calques de dessin qui étend les calques de base (voir le code D.1.2).

\begin{figure}\javafile[Exemple de création d'un nouveau calque. Le calque de dessin]{drawlayer}
\end{figure}

Cette nouvelle classe de calques hérite des propriétés de la classe MaggLiteLayer (redimensionnement automatique, adaptation à la forme du composant conteneur, transparence, pliage, etc.) et surcharge ses méthodes graphiques pour afficher le dessin sur l'objet. Pour le dessin, elle implémente les méthodes de l'interface MaggLiteDraw afin d'être compatible avec les interactions de dessin qui seront définies par un nouveau dispositif d'interaction ou un nouvel outil interne (voir la section D.3.1 de cette annexe).

Il suffit alors d'associer un outil interne à une instance de ce calque et de l'ajouter dans un objet graphique pour pouvoir dessiner sur l'objet, sans avoir eu à modifier son code ou ses propriétés (voir code D.1.2).

\begin{figure}\javafile[Utilisation du calque de dessin]{drawlayer2}
\end{figure}

Cette approche permet d'étendre et de réutiliser les capacités internes des objets. Elle s'avère par contre inadaptée à l'extension des capacités des objets pour une interaction directe (qui doivent agir sur l'objet). Pour cela, la méthode précédente, par héritage, est appropriée bien que favorisant la redondance du code. Il serait alors intéressant d'étudier l'utilisation de la programmation par aspects pour l'extension des objets graphiques. Toutefois, cette technologie nous semble trop limitée à l'heure actuelle, en particulier au niveau des performances dans le cadre d'applications graphiques et hautement interactives comme celle que nous proposons. Cela en fait tout de même une piste pour compléter les mécanismes de notre boîte à outils afin de proposer trois approches d'extension complémentaires:

  1. l'héritage, pour créer de nouvelles classes d'objets graphiques à partir des classes de base;
  2. les aspects, pour étendre certaines capacités que nous qualifierons d'internes de classes d'objets graphiques sans avoir à étendre les classes de bas niveau ou à répéter le code dans plusieurs classes;
  3. les calques pour ajouter des capacités graphiques et interactives à des objets graphiques de manière modulaire et dynamique.

stuf
2005-09-06