# Minipaint ## TP3 – Le patron Adapter ### đŸ›Œ IShape --- Pour diffĂ©rentes raisons, l’architecte souhaite que l’application utilise l’interface `IShape` (fournie ci-dessous) pour manipuler les formes. Il aurait pu nous prĂ©venir avant
 La classe `Shape` n’implĂ©mente pas cette interface ! đŸ˜© đŸ€” Le problĂšme, c’est qu’on ne peut pas modifier la classe `Shape` et les classes qui en hĂ©ritent, comme `Ellipse`, et `Rectangle`, car elles font partie de l’API JavaFx. On pourrait bien sĂ»r Ă©tendre toutes les sous-classes qu’on utilise (`Ellipse`, `Rectangle`, etc.) et faire en sorte que ces sous-classes hĂ©ritent de l’interface. >**đŸ–„ TODO** > > - Expliquez pourquoi ce n’est pas une bonne solution. đŸ’©
L'interface `IShape` en question : import javafx.scene.layout.Pane; public interface IShape { boolean isSelected(); void setSelected(boolean selected); boolean isOn(double x, double y); void offset(double x, double y); void addShapeToPane(Pane pane); void removeShapeFromPane(Pane pane); }
Une meilleure solution consiste Ă  utiliser le patron *Adapter*. Documentez-vous sur ce patron. Vous pouvez notamment regarder la [vidĂ©o de Christopher Okhravi](https://youtu.be/2PKQtcJjYvc) đŸ“ș Si vous avez bien suivi : - l’interface cible est donc l’interface `IShape` ; - la classe Ă  adapter est la classe `Shape` ; - et l’adapteur reste donc Ă  crĂ©er. >**đŸ–„ TODO** > > - Commencez par importer (ou crĂ©er) l’interface `IShape`. > - CrĂ©ez une classe `ShapeAdapter`, qui sera notre adapteur, et qui implĂ©mente donc l’interface `IShape`. Elle contiendra pour l’instant un attribut de type `Shape` (la forme rĂ©ellement dessinĂ©e, qui est la classe Ă  adapter). Cet attribut sera fourni au moment de l’instanciation, en paramĂštre du constructeur. Pour l’instant, nous allons laisser vides le corps des mĂ©thodes dĂ©finies par l’interface `IShape`.
Ensuite, dans toute l’application, il faut utiliser l’interface `IShape` plutĂŽt que la classe `Shape`. >**đŸ–„ TODO** > > - Dans la classe `DrawingPane` : > > - la classe doit plutĂŽt implĂ©menter l’interface `Iterable` ; > - l’attribut `shapes` doit ĂȘtre de type `List` ; > - et la mĂ©thode `addShape` doit prendre un objet de type `IShape` en paramĂštre. La ligne > this.getChildren().add(shape); > sera remplacĂ©e par un appel Ă  la mĂ©thode `addShapeToPane()` de l’interface `IShape`. Le corps de cette mĂ©thode (dans la classe `ShapeAdapter`) contiendra la ligne supprimĂ©e, moyennant quelques ajustements pour que ce soit la forme rĂ©elle (adaptĂ©e) qui soit ajoutĂ©e Ă  la liste des « `children` » du `DrawingPane`. > > - Du coup, dans la classe `ShapeButtonHandler`, la mĂ©thode `createShape` doit elle aussi retourner un objet de type `IShape`. Modifiez les classes qui implĂ©mentent cette mĂ©thode pour qu’elles retournent une nouvelle instance de `ShapeAdapter`, contenant une rĂ©fĂ©rence Ă  la forme rĂ©elle (Rectangle, Ellipse, ou autre). > - ProcĂ©dez de mĂȘme dans les autres classes. ProblĂšme : l’interface `IShape` ne dispose pas des mĂ©thodes `getBoundsInParent()`, `getTranslateX()`, `getTranslateY()`, `setTranslateX()`, `setTranslateY()`, auxquelles on fait appel dans la classe `MouseMoveHandler`. C’est normal, ces mĂ©thodes Ă©taient spĂ©cifiques Ă  la classe `Shape` de JavaFx. Il faut donc les remplacer, en utilisant les mĂ©thodes disponibles dans l’interface `IShape`. Profitez-en pour implĂ©menter le corps de ces mĂ©thodes dans la classe `ShapeAdapter` : > - La mĂ©thode `offset(double x, double y)`, qui doit dĂ©caler la `Shape` de `x` en abscisses et `y` en ordonnĂ©es. > - La mĂ©thode `isOn(double x, double y)`, qui doit retourner `true` si le point dont les coordonnĂ©es passĂ©es en paramĂštre se trouve sur la forme rĂ©elle (ce qui pourra ĂȘtre vĂ©rifiĂ© en appelant la mĂ©thode `getBoundsInParent().contains(x,y)` sur la forme rĂ©elle). > > On va ajouter un retour visuel pour voir quelle forme est dĂ©placĂ©e. Pour cela, il suffit de dĂ©finir une nouvelle classe CSS et de l’ajouter ou de la retirer Ă  la forme rĂ©elle dans la mĂ©thode `setSelected` de la classe `ShapeAdapter`. Ensuite, il faut appeler la mĂ©thode `setSelected` depuis le bon endroit dans le `MouseMoveHandler`. Attention, il faut aussi penser Ă  « dĂ©-sĂ©lectionner » les formes au bon moment
 NB : la classe CSS peut ĂȘtre dĂ©finie ainsi (mais vous ĂȘtes libre de choisir un autre style
) : .selected{ -fx-effect:dropshadow(gaussian,cornflowerblue,8,0.7,0,0); }
### 🖇 SĂ©lection et dĂ©placement multiple --- La classe `MouseMoveHandler` permet de dĂ©placer une forme sur laquelle on a cliquĂ©. Cette classe implĂ©mente le patron *Observer* pour « Ă©couter » les Ă©vĂ©nements `MousePressed`, `MouseDragged` et `MouseReleased` dĂ©clenchĂ©s au niveau du `DrawingPane`. Nous allons nous inspirer de cette classe pour pouvoir sĂ©lectionner une ou plusieurs formes (et par la suite leur appliquer diffĂ©rentes actions). >**đŸ–„ TODO** > > - Commencez par crĂ©er un diagramme de sĂ©quence permettant de comprendre le fonctionnement de la mĂ©thode `handle(MouseEvent)` de la classe `MouseMoveHandler`. > - CrĂ©ez la classe `SelectionHandler`, sur le modĂšle de la classe `MouseMoveHandler`. Dans cette nouvelle classe, on ne dĂ©placera pas les formes. On ne fera que les sĂ©lectionner ou les dĂ©sĂ©lectionner. > - Pensez Ă  crĂ©er une instance de la classe `SelectionHandler` dans le `DrawingPane`.
Pour l’instant, une seule forme peut ĂȘtre sĂ©lectionnĂ©e. Nous allons maintenant modifier la classe `SelectionHandler` pour pouvoir sĂ©lectionner plusieurs formes en mĂȘme temps, sur le modĂšle de ce qui se passe dans beaucoup d’applications. >**đŸ–„ TODO** > > - Modifiez le type de l’attribut `selectedShape` pour qu’il puisse stocker une liste de `IShape` plutĂŽt qu’une seule forme. > - ImplĂ©mentez le remplissage et le vidage de la liste. Le principe de fonctionnement est le suivant : lorsqu’un clic sur une forme est dĂ©tectĂ© : si la touche MAJ est enfoncĂ©e, on ajoute cette forme Ă  la liste de sĂ©lection (ou on la retire de la sĂ©lection si elle y Ă©tait dĂ©jĂ ). Sinon, on vide la liste et on y met seulement la forme sĂ©lectionnĂ©e. Si on clique ailleurs que sur une forme, la sĂ©lection est vidĂ©e.
Maintenant, on peut sĂ©lectionner une ou plusieurs formes. Du coup, il serait intĂ©ressant de modifier la classe `MouseMoveHandler`, pour qu’elle puisse dĂ©placer d’un seul coup toutes les formes sĂ©lectionnĂ©es. >**đŸ–„ TODO** > > - Commencez par crĂ©er la mĂ©thode `getSelection()`, dans la classe `DrawingPane`, qui retourne la liste des formes sĂ©lectionnĂ©es par le `SelectionHandler`. RĂ©flĂ©chissez au type de retour de cette mĂ©thode. > - Dans la classe `MouseMoveHandler`, remplacez l’usage de l’attribut `selectedShape`. La classe `MouseMoveHandler` n’a plus besoin de « dĂ©tecter » la forme qui a Ă©tĂ© cliquĂ©e (c’est le `SelectionHandler` qui s’en charge). À la place, utilisez la mĂ©thode `getSelection()` crĂ©Ă©e prĂ©cĂ©demment pour rĂ©cupĂ©rer toutes les formes sĂ©lectionnĂ©es et les dĂ©placer.
À prĂ©sent, on peut dĂ©placer plusieurs formes en mĂȘme temps. Bravo ! đŸ•șđŸ»
[🔙 Retour](../README.md)