# 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)