Tutoriel 6: projet pong minimal #
Étape 1) créer le projet pong06 à partir de dep06
#
-
Dans un GitBash à la racine du dépôt Git
$ cp -rf dep06 pong06 -
Modifier
settings.gradlepour ajouter l’includeinclude 'pong06' -
Vérifier que
pong06s’exécute$ sh gradlew pong06 -
Dans VSCode, faire un clean workspace pour afficher le projet
pong06
Étape 2) implanter le bon modèle #
ModeleFileAttente
#
-
Avec F2, renommer la classe
ModeleA=>ModeleFileAttente
-
Avec F2, renommer la classe
ValeurA=>PartieEnFile
-
Modifier
ModeleFileAttente-
avec F2, renommer l’attribut
valeurs=>parties
-
s’assurer aussi d’avoir l’attribut
prochainId
public class ModeleFileAttente implements Model, WatchJson, WriteObjectGraph { private int prochainId = 0; private List<PartieEnFile> parties = new ArrayList<>(); // ... -
-
Modifier
ModeleFileAttente.methodeAavec des valeurs temporairespublic void methodeA(String paramA, int paramB) { parties.add(new PartieEnFile(String.valueOf(prochainId++), null, paramA, paramB, null, null, 0)); } -
Modifier
ModeleFileAttente.methodeC(affichage sur la Vue)public void methodeC(VueA vueA) { vueA.methodeE(); for(PartieEnFile valeur : valeurs) { vueA.methodeF(valeur); } }
PartieEnFile
#
-
Modifier
PartieEnFileselon le document de conception:public class PartieEnFile implements ModelValue { private String idPartie; private String idJoueurA; private String nomJoueurA; private int scoreJoueurA; private String idJoueurB; private String nomJoueurB; private int scoreJoueurB; // ... -
Modifier le constructeur:
public PartieEnFile(String idPartie, String idJoueurA, String nomJoueurA, int scoreJoueurA, String idJoueurB, String nomJoueurB, int scoreJoueurB) { this.idPartie = idPartie; this.idJoueurA = idJoueurA; this.nomJoueurA = nomJoueurA; this.scoreJoueurA = scoreJoueurA; this.idJoueurB = idJoueurB; this.nomJoueurB = nomJoueurB; this.scoreJoueurB = scoreJoueurB; } -
Modifier
PartieEnFile.methodeA(affichage sur le fragment)public void methodeA(FragmentA paramA) { paramA.methodeA(nomJoueurA); if(nomJoueurB == null){ paramA.methodeB("???"); }else{ paramA.methodeB(nomJoueurB); } paramA.methodeC(String.format("%d-%d", scoreJoueurA, scoreJoueurB)); }-
Avec Ctrl+., changer la signature de
methodeB
-
Avec Ctrl+., créer la
methodeC
-
-
S’assurer que la méthode
FragmentA.methodeCne lance pas d’exceptionpublic class FragmentA extends ViewFragmentFx { // ... public void methodeC(String format) { // Retirer ce que VSCode a généré // TODO Auto-generated method stub // throw new UnsupportedOperationException("Unimplemented method 'methodeC'"); } }
Vérifier l’état courant du projet #
-
Dans un GitBash à la racine du dépôt Git
$ sh gradlew pong06
Étape 3) adapter les vues et le fragment #
Modifier les fichiers .properties
#
-
fr.propertiesajouter=Ajouter jouer=Jouer quitter=Quitter partie -
en.propertiesajouter=Add jouer=Play quitter=Exit Game
Adapter le fichier vue_a.fxml
#
-
Modifier l’alignement pour le
VBoxracine<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="depart.frontal.vues.VueA" styleClass="conteneur,v,top-center"> <!-- changer à top-center --> -
Retirer le
boutonAet leslabelA,labelB<!-- ... --> <Button fx:id="boutonA" text="%texteA"> </Button> <!-- retirer --> <Pane styleClass="espacement,moyen" VBox.vgrow="ALWAYS"/> <!-- retirer --> <Label fx:id="labelA"/> <!-- retirer --> <Pane styleClass="espacement,petit"/> <!-- retirer --> <Label fx:id="labelB"/> <!-- retirer --> -
Modifier le
%varduboutonB<Button fx:id="boutonB" text="%ajouter" /> <!-- modifier --> -
Déplacer le
boutonBen haut duconteneurA<Button fx:id="boutonB" text="%texteB"> </Button> <!-- déplacer en haut du conteneur --> <Pane styleClass="espacement,petit"/> <HBox styleClass="conteneur,h,center-left" fx:id="conteneurA"/> -
Modifier le
conteneurAen conteneur vertical (et ajouter unHBox.hgrow)<VBox <!-- transformer en VBox --> styleClass="conteneur,v,top-center" <!-- v --> fx:id="conteneurA" HBox.hgrow="ALWAYS"/> <!-- ajouter HBox.hgrow --> -
Enrober le
conteneurAdans unHBoxavec des espacements<HBox> <!-- HBox autour --> <Pane styleClass="espacement,moyen"/> <VBox styleClass="conteneur,v,top-center" fx:id="conteneurA" HBox.hgrow="ALWAYS"/> <Pane styleClass="espacement,moyen"/> </HBox> <!-- du contenurA -->
Adapter la classe VueA
#
-
Retirer
labelA,labelBetboutonA- retirer aussi le code où
labelA,labelBetboutonAétaient utlisé
- retirer aussi le code où
-
Modifier
methodeFpour ajouter aussi un espacementpublic void methodeF(PartieEnFile paramC) { FragmentA varA = viewLoader.createView(); paramC.methodeA(varA); if(conteneurA.getChildren().size() > 0){ Pane espacement = new Pane(); espacement.getStyleClass().add("espacement"); espacement.getStyleClass().add("petit"); conteneurA.getChildren().add(espacement); } conteneurA.getChildren().add(varA.rootNode()); }
Adapter le fichier vue_b.fxml
#
-
Retirer le Label et modifier le
%vardu bouton<!-- ... --> <Label styleClass="label" text="%texteA"> </Label> <!-- retirer --> <Pane styleClass="espacement,moyen"/> <!-- retirer --> <Button fx:id="boutonA" text="%quitter"> </Button> <!-- modifier -->
Adapter le fichier fragment_a.fxml
#
-
Transformer le
VBoxracine enHBox+ ajouter la classepartie-en-file<HBox xmlns:fx="http://javafx.com/fxml" fx:controller="depart.frontal.fragments.FragmentA" styleClass="conteneur,h,center,partie-en-file"> <!-- changer de v à h --> <!-- ajouter partie-en-file --> <!-- ... --> </HBox> -
Ajouter un
boutonAaprès lelabelB- (et ajouter
HBox.hgrowà l’espacement pour pousser le bouton à droite)
<Label fx:id="labelB"/> <Pane styleClass="espacement,petit"/> <!-- ajouter le HBox.hgrow --> <Button fx:id="boutonA" text="%jouer"/> <!-- ajouter --> - (et ajouter
-
Envelopper les labels et le bouton dans un
HBox, puis unVBox(avec des espacements)<VBox styleClass="center" HBox.hgrow="ALWAYS"> <Pane styleClass="espacement,petit"/> <HBox styleClass="center" HBox.hgrow="ALWAYS"> <Pane styleClass="espacement,petit"/> <!-- labels et bouton ici --> <Pane styleClass="espacement,petit"/> </HBox> </VBox>
Adapter la classe FragmentA
#
-
Ajouter le
boutonApublic class FragmentA extends ViewFragmentFx { //... @FXML private Button boutonA; -
Déclencher l’événement
EvtBavec leboutonApublic class FragmentA extends ViewFragmentFx { // ... @Override public void initialize() { // ... Ntro.assertNotNull(boutonA); boutonA.setOnAction(evtFx -> { Ntro.newEvent(EvtB.class).trigger(); }); } -
Ajouter des attributs
texteA,texteBpour sauvegarder le nom des joueurspublic class FragmentA extends ViewFragmentFx { // ... private String texteA = ""; private String texteB = ""; -
Modifier les
methodeA,methodeBpour afficher le nom des joueurspublic class FragmentA extends ViewFragmentFx { // ... public void methodeA(String paramA) { texteA = paramA; labelA.setText(texteA + " VS " + texteB); } public void methodeB(String paramB) { texteB = paramB; labelA.setText(texteA + " VS " + texteB); } -
Considérer la ligne de code
labelA.setText(texteA + " VS " + texteB);qui se répète ci-haut-
sélectionner cette ligne dans la
methodeA -
avec Ctrl+. => Extract method, extraire la méthode
afficherNoms
-
vérifier que la ligne a été remplacée par un appel à
afficherNomsdansmethodeAetmethodeB
-
-
Utiliser la
methodeCpour afficher le scorepublic class FragmentA extends ViewFragmentFx { // ... public void methodeC(String format) { labelB.setText(format); }
Vérifier le comportement et l’affichage #
-
Effacer le modèle actuel
$ rm -rf pong06/_storage/models -
Démarrer avec un modèle vide
$ sh gradlew pong06
Étape 5) Ajouter un fichier prod.css et le déclarer dans FrontalPong.java
#
-
Télécharger le fichier prod.css et le copier dans
pong06/src/main/resources/style -
Dans
FrontalPong.java, modifier la déclaration du CSS://registrar.registerStylesheet("/style/dev.css"); registrar.registerStylesheet("/style/prod.css");
Vérifier le comportement et l’affichage #
-
Effacer le modèle actuel
$ rm -rf pong06/_storage/models -
Démarrer avec un modèle vide
$ sh gradlew pong06
Étape 5) implanter le bon message #
-
Avec F2, renommer la classe
MsgA=>MsgAjouterPartie
-
Modifier les attributs du message
public class MsgAjouterPartie extends Message<MsgAjouterPartie> { private String idJoueurA; private String nomJoueurA; private int scoreJoueurA; private String idJoueurB; private String nomJoueurB; private int scoreJoueurB; -
Avec Ctrl+., générer les setters
public void setIdJoueurA(String idJoueurA) { this.idJoueurA = idJoueurA; } public void setNomJoueurA(String nomJoueurA) { this.nomJoueurA = nomJoueurA; } public void setScoreJoueurA(int scoreJoueurA) { this.scoreJoueurA = scoreJoueurA; } public void setIdJoueurB(String idJoueurB) { this.idJoueurB = idJoueurB; } public void setNomJoueurB(String nomJoueurB) { this.nomJoueurB = nomJoueurB; } public void setScoreJoueurB(int scoreJoueurB) { this.scoreJoueurB = scoreJoueurB; } -
Adapter les setters pour retourner
thispublic MsgAjouterPartie setIdJoueurA(String idJoueurA) { this.idJoueurA = idJoueurA; return this; } public MsgAjouterPartie setNomJoueurA(String nomJoueurA) { this.nomJoueurA = nomJoueurA; return this; } public MsgAjouterPartie setScoreJoueurA(int scoreJoueurA) { this.scoreJoueurA = scoreJoueurA; return this; } public MsgAjouterPartie setIdJoueurB(String idJoueurB) { this.idJoueurB = idJoueurB; return this; } public MsgAjouterPartie setNomJoueurB(String nomJoueurB) { this.nomJoueurB = nomJoueurB; return this; } public MsgAjouterPartie setScoreJoueurB(int scoreJoueurB) { this.scoreJoueurB = scoreJoueurB; return this; } -
Modifier l’appel à la
methodeA
-
Utiliser Ctrl+. pour modifier la signature de la
methodeA:
-
Finaliser le code de
ModeleFileAttente.methodeApublic void methodeA(String idJoueurA, String nomJoueurA, int scoreJoueurA, String idJoueurB, String nomJoueurB, int scoreJoueurB) { parties.add(new PartieEnFile(String.valueOf(prochainId++), idJoueurA, nomJoueurA, scoreJoueurA, idJoueurB, nomJoueurB, scoreJoueurB)); } -
Modifier la création du message dans la
VueAboutonB.setOnAction(evtFx -> { Ntro.newMessage(MsgAjouterPartie.class) .setNomJoueurA(MaquetteDepart.chaineAuHasard()) .setScoreJoueurA(MaquetteDepart.entierAuHasard()) .setNomJoueurB(MaquetteDepart.chaineAuHasard()) .setScoreJoueurB(MaquetteDepart.entierAuHasard()) .send(); });
Étape 6) adapter MaquetteDepart
#
-
Avec F2, renommer la classe
MaquetteDepart=>MaquettePartie
-
Modifier le code de
MaquetteDepartpublic class MaquettePartie { private static String idJoueur = "alice"; public static String idJoueur() { List<String> choixDeId = List.of("alice", "bob", "chaaya", "dominic", "élisabeth", "firas", "gregson", "mehdi", "louis", "marcel", "ashwin", "ichiro", "jun"); idJoueur = Ntro.random().choice(choixDeId); return idJoueur; } public static String nomJoueur() { return idJoueur.substring(0,1).toUpperCase() + idJoueur.substring(1); } public static int scoreAuHasard() { return Ntro.random().nextInt(10); } } -
Modifier l’envoi du message dans
VueAboutonB.setOnAction(evtFx -> { Ntro.newMessage(MsgAjouterPartie.class) .setIdJoueurA(MaquettePartie.idJoueur()) .setNomJoueurA(MaquettePartie.nomJoueur()) .setScoreJoueurA(MaquettePartie.scoreAuHasard()) .setIdJoueurB(MaquettePartie.idJoueur()) .setNomJoueurB(MaquettePartie.nomJoueur()) .setScoreJoueurB(MaquettePartie.scoreAuHasard()) .send(); });
Vérifier l’état courant du projet #
-
Effacer le modèle actuel
$ rm -rf pong06/_storage/models -
Dans un GitBash à la racine du dépôt Git
$ sh gradlew pong06