Exact picture seems to work
This commit is contained in:
@@ -5,6 +5,16 @@ package ch.gtache.fro.gui;
|
||||
*/
|
||||
public interface SettingsController extends Controller {
|
||||
|
||||
/**
|
||||
* Imports settings from a file
|
||||
*/
|
||||
void importSettings();
|
||||
|
||||
/**
|
||||
* Exports settings to a file
|
||||
*/
|
||||
void exportSettings();
|
||||
|
||||
@Override
|
||||
SettingsModel model();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package ch.gtache.fro.practice.gui;
|
||||
|
||||
import ch.gtache.fro.gui.Model;
|
||||
|
||||
/**
|
||||
* Model for the practice view - exact guess
|
||||
*/
|
||||
public interface PracticeExactModel extends Model {
|
||||
public interface PracticeExactModel extends PracticeQuestionModel {
|
||||
|
||||
/**
|
||||
* Returns the user's current guess
|
||||
@@ -19,5 +17,5 @@ public interface PracticeExactModel extends Model {
|
||||
*
|
||||
* @param guess The guess
|
||||
*/
|
||||
void guess(String guess);
|
||||
void setGuess(String guess);
|
||||
}
|
||||
|
||||
@@ -11,4 +11,9 @@ public interface PracticeGuessController extends Controller {
|
||||
* Confirms the current guess
|
||||
*/
|
||||
void confirm();
|
||||
|
||||
/**
|
||||
* Proceeds to the next question/show the result page
|
||||
*/
|
||||
void next();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
package ch.gtache.fro.practice.gui;
|
||||
|
||||
import ch.gtache.fro.gui.Model;
|
||||
import ch.gtache.fro.practice.PracticeRun;
|
||||
|
||||
/**
|
||||
* Model for the practice view
|
||||
*/
|
||||
public interface PracticeModel extends Model {
|
||||
|
||||
/**
|
||||
* Returns the current practice run
|
||||
*
|
||||
* @return The practice run
|
||||
*/
|
||||
PracticeRun practiceRun();
|
||||
|
||||
/**
|
||||
* Sets the practice run
|
||||
*
|
||||
* @param practiceRun The practice run
|
||||
*/
|
||||
void setPracticeRun(PracticeRun practiceRun);
|
||||
}
|
||||
|
||||
@@ -29,5 +29,5 @@ public interface PracticeMultichoiceModel extends Model {
|
||||
*
|
||||
* @param selected The selected bird
|
||||
*/
|
||||
void selected(Bird selected);
|
||||
void setSelected(Bird selected);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package ch.gtache.fro.practice.gui;
|
||||
|
||||
import ch.gtache.fro.Picture;
|
||||
import ch.gtache.fro.gui.Model;
|
||||
|
||||
/**
|
||||
* Represents a model for a practice view with a picture
|
||||
*/
|
||||
public interface PracticePictureModel extends Model {
|
||||
public interface PracticePictureModel extends PracticeQuestionModel {
|
||||
|
||||
/**
|
||||
* Returns the picture to guess
|
||||
@@ -20,5 +19,5 @@ public interface PracticePictureModel extends Model {
|
||||
*
|
||||
* @param picture The picture
|
||||
*/
|
||||
void picture(Picture picture);
|
||||
void setPicture(Picture picture);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package ch.gtache.fro.practice.gui;
|
||||
|
||||
import ch.gtache.fro.Bird;
|
||||
import ch.gtache.fro.gui.Model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Model for the practice questions views
|
||||
*/
|
||||
public interface PracticeQuestionModel extends Model {
|
||||
|
||||
/**
|
||||
* Returns whether the view is currently showing the question result
|
||||
*
|
||||
* @return The result
|
||||
*/
|
||||
boolean isShowingResult();
|
||||
|
||||
/**
|
||||
* Returns the list of guesses made
|
||||
*
|
||||
* @return The guesses
|
||||
*/
|
||||
List<Bird> guesses();
|
||||
|
||||
/**
|
||||
* Returns the number of guesses made
|
||||
*
|
||||
* @return The guess count
|
||||
*/
|
||||
default int guessesCount() {
|
||||
return guesses().size();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,11 @@ import ch.gtache.fro.gui.Controller;
|
||||
*/
|
||||
public interface PracticeResultController extends Controller {
|
||||
|
||||
/**
|
||||
* Closes the practice result view
|
||||
*/
|
||||
void confirm();
|
||||
|
||||
@Override
|
||||
PracticeResultModel model();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,52 @@
|
||||
package ch.gtache.fro.practice.gui;
|
||||
|
||||
import ch.gtache.fro.gui.Model;
|
||||
import ch.gtache.fro.practice.PracticeResult;
|
||||
|
||||
/**
|
||||
* Model for the practice result view
|
||||
*/
|
||||
public interface PracticeResultModel extends Model {
|
||||
|
||||
/**
|
||||
* Gets the practice result
|
||||
*
|
||||
* @return the practice result
|
||||
*/
|
||||
PracticeResult result();
|
||||
|
||||
/**
|
||||
* Sets the practice result
|
||||
*
|
||||
* @param result the practice result
|
||||
*/
|
||||
void setResult(PracticeResult result);
|
||||
|
||||
/**
|
||||
* The text for the success label
|
||||
*
|
||||
* @return The text
|
||||
*/
|
||||
String successLabel();
|
||||
|
||||
/**
|
||||
* The text for the failure label
|
||||
*
|
||||
* @return The text
|
||||
*/
|
||||
String failureLabel();
|
||||
|
||||
/**
|
||||
* The text for the label of successful guesses
|
||||
*
|
||||
* @return The text
|
||||
*/
|
||||
String successListLabel();
|
||||
|
||||
/**
|
||||
* The text for the label of failed guesses
|
||||
*
|
||||
* @return The text
|
||||
*/
|
||||
String failureListLabel();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package ch.gtache.fro.practice.gui;
|
||||
|
||||
import ch.gtache.fro.gui.Model;
|
||||
import ch.gtache.fro.practice.PracticeType;
|
||||
import ch.gtache.fro.practice.PracticeParameters;
|
||||
import ch.gtache.fro.practice.QuestionType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -15,21 +16,49 @@ public interface PracticeSettingsModel extends Model {
|
||||
*
|
||||
* @return The list of practice types
|
||||
*/
|
||||
List<PracticeType> practiceTypes();
|
||||
List<QuestionType> questionTypes();
|
||||
|
||||
/**
|
||||
* Returns the currently selected practice type
|
||||
*
|
||||
* @return The practice type
|
||||
*/
|
||||
PracticeType practiceType();
|
||||
QuestionType questionType();
|
||||
|
||||
/**
|
||||
* Sets the practice type
|
||||
*
|
||||
* @param practiceType The practice type
|
||||
* @param questionType The practice type
|
||||
*/
|
||||
void practiceType(PracticeType practiceType);
|
||||
void setQuestionType(QuestionType questionType);
|
||||
|
||||
/**
|
||||
* Returns the number of unsuccessful guesses before a question is considered as failed
|
||||
*
|
||||
* @return The number of unsuccessful guesses
|
||||
*/
|
||||
int guessesNumber();
|
||||
|
||||
/**
|
||||
* Sets the number of unsuccessful guesses before a question is considered as failed
|
||||
*
|
||||
* @param guessesNumber The number of unsuccessful guesses
|
||||
*/
|
||||
void setGuessesNumber(int guessesNumber);
|
||||
|
||||
/**
|
||||
* Returns the number of questions
|
||||
*
|
||||
* @return The number of questions
|
||||
*/
|
||||
int questionsNumber();
|
||||
|
||||
/**
|
||||
* Sets the number of questions
|
||||
*
|
||||
* @param questionsNumber The number of questions
|
||||
*/
|
||||
void setQuestionsNumber(int questionsNumber);
|
||||
|
||||
/**
|
||||
* Returns true if the practice type has suggestions
|
||||
@@ -50,5 +79,12 @@ public interface PracticeSettingsModel extends Model {
|
||||
*
|
||||
* @param suggestionsNumber The number of suggestions
|
||||
*/
|
||||
void suggestionsNumber(int suggestionsNumber);
|
||||
void setSuggestionsNumber(int suggestionsNumber);
|
||||
|
||||
/**
|
||||
* Returns the current practice parameters
|
||||
*
|
||||
* @return The practice parameters
|
||||
*/
|
||||
PracticeParameters practiceParameters();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package ch.gtache.fro.practice.gui;
|
||||
|
||||
import ch.gtache.fro.Sound;
|
||||
import ch.gtache.fro.gui.Model;
|
||||
|
||||
/**
|
||||
* Represents a model for a practice view with a sound
|
||||
*/
|
||||
public interface PracticeSoundModel extends Model {
|
||||
public interface PracticeSoundModel extends PracticeQuestionModel {
|
||||
|
||||
/**
|
||||
* Returns the sound to guess
|
||||
@@ -20,5 +19,5 @@ public interface PracticeSoundModel extends Model {
|
||||
*
|
||||
* @param sound The sound
|
||||
*/
|
||||
void sound(Sound sound);
|
||||
void setSound(Sound sound);
|
||||
}
|
||||
|
||||
@@ -28,5 +28,5 @@ public interface PracticeSoundMultichoicePictureModel extends PracticeSoundModel
|
||||
*
|
||||
* @param selected The selected picture
|
||||
*/
|
||||
void selected(Picture selected);
|
||||
void setSelected(Picture selected);
|
||||
}
|
||||
@@ -3,9 +3,8 @@
|
||||
*/
|
||||
module ch.gtache.fro.gui.core {
|
||||
requires transitive ch.gtache.fro.gui.api;
|
||||
requires ch.gtache.fro.core;
|
||||
requires jakarta.inject;
|
||||
requires dagger;
|
||||
requires transitive jakarta.inject;
|
||||
requires transitive ch.gtache.fro.core;
|
||||
|
||||
exports ch.gtache.fro.gui.impl;
|
||||
exports ch.gtache.fro.modules.gui.impl;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
fetch.button.all.label=Télécharger tous
|
||||
fetch.button.fetch.label=Télécharger
|
||||
fetch.button.all.label=Télécharger tous
|
||||
fetch.button.fetch.label=Télécharger
|
||||
fetch.providers.label=Sources
|
||||
@@ -1,3 +1,3 @@
|
||||
main.tab.fetch.label=Téléchargement
|
||||
main.tab.practice.label=Entraînement
|
||||
main.tab.settings.label=Paramètres
|
||||
main.tab.fetch.label=Téléchargement
|
||||
main.tab.practice.label=Entraînement
|
||||
main.tab.settings.label=Paramètres
|
||||
@@ -1,5 +1,5 @@
|
||||
settings.table.column.bird=Oiseau
|
||||
settings.table.column.enabled=Activé
|
||||
settings.table.column.enabled=Activé
|
||||
settings.table.column.fetchers=Fournisseurs
|
||||
settings.table.column.pictures=Images
|
||||
settings.table.column.sounds=Sons
|
||||
@@ -1 +1,2 @@
|
||||
practice.picture.exact.validate.button.label=Confirm
|
||||
practice.picture.exact.next.button.label=Next
|
||||
practice.picture.exact.validate.button.label=Confirm
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
practice.picture.exact.next.button.label=Next
|
||||
practice.picture.exact.validate.button.label=Confirm
|
||||
@@ -1 +1,2 @@
|
||||
practice.picture.exact.next.button.label=Suivant
|
||||
practice.picture.exact.validate.button.label=Confirmer
|
||||
@@ -1 +1,2 @@
|
||||
practice.picture.multichoice.validate.button.label=Confirm
|
||||
practice.picture.multichoice.next.button.label=Next
|
||||
practice.picture.multichoice.validate.button.label=Confirm
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
practice.picture.multichoice.next.button.label=Next
|
||||
practice.picture.multichoice.validate.button.label=Confirm
|
||||
@@ -1 +1,2 @@
|
||||
practice.picture.multichoice.next.button.label=Suivant
|
||||
practice.picture.multichoice.validate.button.label=Confirmer
|
||||
@@ -1,3 +1,4 @@
|
||||
practice.result.confirm.button.label=Confirm
|
||||
practice.result.failure.label=Failed guesses
|
||||
practice.result.label=Practice result
|
||||
practice.result.success.label=Correct guesses
|
||||
practice.result.success.label=Correct guesses
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
practice.result.confirm.button.label=Confirm
|
||||
practice.result.failure.label=Failed guesses
|
||||
practice.result.label=Practice result
|
||||
practice.result.success.label=Correct guesses
|
||||
@@ -1,3 +1,4 @@
|
||||
practice.result.failure.label=Choix incorrects
|
||||
practice.result.confirm.button.label=Confirmer
|
||||
practice.result.failure.label=Réponses incorrectes
|
||||
practice.result.label=Résultat
|
||||
practice.result.success.label=Choix corrects
|
||||
practice.result.success.label=Réponses correctes
|
||||
@@ -1,3 +1,5 @@
|
||||
practice.settings.guesses.number.label=Number of available guesses
|
||||
practice.settings.questions.number.label=Number of questions
|
||||
practice.settings.start.button.label=Start
|
||||
practice.settings.suggestions.number.label=Number of suggestions
|
||||
practice.settings.type.label=Practice type
|
||||
practice.settings.type.label=Practice type
|
||||
@@ -1,3 +1,5 @@
|
||||
practice.settings.guesses.number.label=Number of available guesses
|
||||
practice.settings.questions.number.label=Number of questions
|
||||
practice.settings.start.button.label=Start
|
||||
practice.settings.suggestions.number.label=Number of suggestions
|
||||
practice.settings.type.label=Practice type
|
||||
@@ -1,3 +1,5 @@
|
||||
practice.settings.start.button.label=Démarrer
|
||||
practice.settings.guesses.number.label=Nombre d'essais disponibles
|
||||
practice.settings.questions.number.label=Nombre de questions
|
||||
practice.settings.start.button.label=Démarrer
|
||||
practice.settings.suggestions.number.label=Nombre de suggestions
|
||||
practice.settings.type.label=Type d'entraînement
|
||||
practice.settings.type.label=Type d'entraînement
|
||||
@@ -1 +1,2 @@
|
||||
practice.sound.exact.validate.button.label=Confirm
|
||||
practice.sound.exact.next.button.label=Next
|
||||
practice.sound.exact.validate.button.label=Confirm
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
practice.sound.exact.next.button.label=Next
|
||||
practice.sound.exact.validate.button.label=Confirm
|
||||
@@ -1 +1,2 @@
|
||||
practice.sound.exact.next.button.label=Suivant
|
||||
practice.sound.exact.validate.button.label=Confirmer
|
||||
@@ -1 +1,2 @@
|
||||
practice.sound.multichoice.validate.button.label=Confirm
|
||||
practice.sound.multichoice.next.button.label=Next
|
||||
practice.sound.multichoice.validate.button.label=Confirm
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
practice.sound.multichoice.next.button.label=Next
|
||||
practice.sound.multichoice.validate.button.label=Confirm
|
||||
@@ -1 +1,2 @@
|
||||
practice.sound.multichoice.next.button.label=Suivant
|
||||
practice.sound.multichoice.validate.button.label=Confirmer
|
||||
@@ -1 +1,2 @@
|
||||
practice.sound.multichoice.picture.validate.button.label=Confirm
|
||||
practice.sound.multichoice.picture.next.button.label=Next
|
||||
practice.sound.multichoice.picture.validate.button.label=Confirm
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
practice.sound.multichoice.picture.next.button.label=Next
|
||||
practice.sound.multichoice.picture.validate.button.label=Confirm
|
||||
@@ -1 +1,2 @@
|
||||
practice.sound.multichoice.picture.next.button.label=Suivant
|
||||
practice.sound.multichoice.picture.validate.button.label=Confirmer
|
||||
@@ -1,8 +1,14 @@
|
||||
package ch.gtache.fro.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Bird;
|
||||
import ch.gtache.fro.Fetcher;
|
||||
import ch.gtache.fro.gui.FetchModel;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
/**
|
||||
* FX implementation of {@link FetchModel}
|
||||
@@ -10,8 +16,16 @@ import jakarta.inject.Singleton;
|
||||
@Singleton
|
||||
public final class FXFetchModel implements FetchModel {
|
||||
|
||||
private final ObservableList<Bird> birds;
|
||||
private final ObjectProperty<Bird> selectedBird;
|
||||
private final ObservableList<Fetcher> fetchers;
|
||||
private final ObjectProperty<Fetcher> selectedFetcher;
|
||||
|
||||
@Inject
|
||||
FXFetchModel() {
|
||||
|
||||
this.birds = FXCollections.observableArrayList();
|
||||
this.selectedBird = new SimpleObjectProperty<>();
|
||||
this.fetchers = FXCollections.observableArrayList();
|
||||
this.selectedFetcher = new SimpleObjectProperty<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package ch.gtache.fro.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Bird;
|
||||
import ch.gtache.fro.gui.SettingsController;
|
||||
import ch.gtache.fro.practice.BirdPracticeParameters;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -16,15 +16,25 @@ import java.util.Objects;
|
||||
public final class FXSettingsController implements SettingsController {
|
||||
|
||||
private final FXSettingsModel model;
|
||||
|
||||
|
||||
@FXML
|
||||
private TableView<Bird> table;
|
||||
private TableView<BirdPracticeParameters> table;
|
||||
|
||||
@Inject
|
||||
FXSettingsController(final FXSettingsModel model) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importSettings() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportSettings() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FXSettingsModel model() {
|
||||
return model;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package ch.gtache.fro.gui.fx;
|
||||
|
||||
import ch.gtache.fro.gui.SettingsModel;
|
||||
import ch.gtache.fro.practice.BirdPracticeParameters;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
/**
|
||||
* FX implementation of {@link SettingsModel}
|
||||
@@ -10,7 +13,10 @@ import jakarta.inject.Singleton;
|
||||
@Singleton
|
||||
public final class FXSettingsModel implements SettingsModel {
|
||||
|
||||
private final ObservableList<BirdPracticeParameters> birdParameters;
|
||||
|
||||
@Inject
|
||||
FXSettingsModel() {
|
||||
this.birdParameters = FXCollections.observableArrayList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Bird;
|
||||
import ch.gtache.fro.practice.PracticeRun;
|
||||
import ch.gtache.fro.practice.gui.PracticeQuestionModel;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyStringProperty;
|
||||
import javafx.beans.property.ReadOnlyStringWrapper;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
/**
|
||||
* Base FX model for practice question views
|
||||
*/
|
||||
public abstract class AbstractFXPracticeQuestionModel implements PracticeQuestionModel {
|
||||
private final ObjectProperty<PracticeRun> practiceRun;
|
||||
private final ReadOnlyStringWrapper progress;
|
||||
private final BooleanProperty showingResult;
|
||||
private final ObservableList<Bird> guesses;
|
||||
|
||||
/**
|
||||
* Instantiates the model
|
||||
*/
|
||||
protected AbstractFXPracticeQuestionModel() {
|
||||
this.practiceRun = new SimpleObjectProperty<>();
|
||||
this.progress = new ReadOnlyStringWrapper();
|
||||
this.showingResult = new SimpleBooleanProperty(false);
|
||||
this.guesses = FXCollections.observableArrayList();
|
||||
this.progress.bind(practiceRun.map(r -> r.currentQuestionIndex() + 1 + "/" + r.parameters().questionsNumber()));
|
||||
}
|
||||
|
||||
ObjectProperty<PracticeRun> practiceRunProperty() {
|
||||
return practiceRun;
|
||||
}
|
||||
|
||||
ReadOnlyStringProperty progressProperty() {
|
||||
return progress.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowingResult() {
|
||||
return showingResult.get();
|
||||
}
|
||||
|
||||
BooleanProperty showingResultProperty() {
|
||||
return showingResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<Bird> guesses() {
|
||||
return guesses;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.practice.gui.PracticeExactModel;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
/**
|
||||
* Base FX model for practice question views with exact answer
|
||||
*/
|
||||
public abstract class AbstractFXPraticeQuestionExactModel extends AbstractFXPracticeQuestionModel implements PracticeExactModel {
|
||||
|
||||
private final StringProperty guess;
|
||||
|
||||
protected AbstractFXPraticeQuestionExactModel() {
|
||||
this.guess = new SimpleStringProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String guess() {
|
||||
return guess.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGuess(String guess) {
|
||||
this.guess.set(guess);
|
||||
}
|
||||
|
||||
StringProperty guessProperty() {
|
||||
return guess;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Bird;
|
||||
import ch.gtache.fro.practice.gui.PracticeMultichoiceModel;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
/**
|
||||
* Base FX model for practice question views with multichoice answers
|
||||
*/
|
||||
public abstract class AbstractFXPraticeQuestionMultichoiceModel extends AbstractFXPracticeQuestionModel implements PracticeMultichoiceModel {
|
||||
|
||||
private final ObservableList<Bird> suggestions;
|
||||
private final ObjectProperty<Bird> selected;
|
||||
|
||||
/**
|
||||
* Instantiates the model
|
||||
*/
|
||||
protected AbstractFXPraticeQuestionMultichoiceModel() {
|
||||
this.suggestions = FXCollections.observableArrayList();
|
||||
this.selected = new SimpleObjectProperty<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<Bird> suggestions() {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bird selected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(final Bird selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
|
||||
ObjectProperty<Bird> selectedProperty() {
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Binds the models.
|
||||
*/
|
||||
@Singleton
|
||||
public class FXPracticeBinder {
|
||||
|
||||
private final FXPracticeModel model;
|
||||
private final FXPracticePictureExactModel pictureExactModel;
|
||||
private final FXPracticePictureMultichoiceModel practicePictureMultichoiceModel;
|
||||
private final FXPracticeSoundExactModel soundExactModel;
|
||||
private final FXPracticeSoundMultichoiceModel soundMultichoiceModel;
|
||||
private final FXPracticeSoundMultichoicePictureModel soundMultichoicePictureModel;
|
||||
private final FXPracticeSettingsModel settingsModel;
|
||||
private final FXPracticeResultModel resultModel;
|
||||
|
||||
@Inject
|
||||
FXPracticeBinder(final FXPracticeModel model, final FXPracticePictureExactModel pictureExactModel,
|
||||
final FXPracticePictureMultichoiceModel practicePictureMultichoiceModel,
|
||||
final FXPracticeSoundExactModel soundExactModel,
|
||||
final FXPracticeSoundMultichoiceModel soundMultichoiceModel,
|
||||
final FXPracticeSoundMultichoicePictureModel soundMultichoicePictureModel,
|
||||
final FXPracticeSettingsModel settingsModel,
|
||||
final FXPracticeResultModel resultModel) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
this.pictureExactModel = Objects.requireNonNull(pictureExactModel);
|
||||
this.practicePictureMultichoiceModel = Objects.requireNonNull(practicePictureMultichoiceModel);
|
||||
this.soundExactModel = Objects.requireNonNull(soundExactModel);
|
||||
this.soundMultichoiceModel = Objects.requireNonNull(soundMultichoiceModel);
|
||||
this.soundMultichoicePictureModel = Objects.requireNonNull(soundMultichoicePictureModel);
|
||||
this.settingsModel = Objects.requireNonNull(settingsModel);
|
||||
this.resultModel = Objects.requireNonNull(resultModel);
|
||||
}
|
||||
|
||||
void createBindings() {
|
||||
model.practiceRunProperty().bindBidirectional(pictureExactModel.practiceRunProperty());
|
||||
model.practiceRunProperty().bindBidirectional(practicePictureMultichoiceModel.practiceRunProperty());
|
||||
model.practiceRunProperty().bindBidirectional(soundExactModel.practiceRunProperty());
|
||||
model.practiceRunProperty().bindBidirectional(soundMultichoiceModel.practiceRunProperty());
|
||||
model.practiceRunProperty().bindBidirectional(soundMultichoicePictureModel.practiceRunProperty());
|
||||
model.practiceRunProperty().bindBidirectional(settingsModel.practiceRunProperty());
|
||||
model.practiceResultProperty().bindBidirectional(resultModel.resultProperty());
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.practice.PracticeRunner;
|
||||
import ch.gtache.fro.practice.gui.PracticeController;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.layout.Pane;
|
||||
|
||||
import java.util.Objects;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* FX implementation of {@link PracticeController}
|
||||
@@ -30,10 +31,14 @@ public final class FXPracticeController implements PracticeController {
|
||||
private Pane practiceSoundMultichoicePicture;
|
||||
|
||||
private final FXPracticeModel model;
|
||||
private final FXPracticeBinder binder;
|
||||
private final PracticeRunner runner;
|
||||
|
||||
@Inject
|
||||
FXPracticeController(final FXPracticeModel model) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
FXPracticeController(final FXPracticeModel model, final FXPracticeBinder binder, final PracticeRunner runner) {
|
||||
this.model = requireNonNull(model);
|
||||
this.binder = requireNonNull(binder);
|
||||
this.runner = requireNonNull(runner);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -47,6 +52,31 @@ public final class FXPracticeController implements PracticeController {
|
||||
}
|
||||
});
|
||||
model.setSelectedPane(practiceSettings);
|
||||
binder.createBindings();
|
||||
|
||||
model.practiceRunProperty().addListener((_, _, newValue) -> {
|
||||
if (newValue == null) {
|
||||
model.setSelectedPane(practiceSettings);
|
||||
} else if (newValue.isFinished()) {
|
||||
final var result = runner.finish(newValue);
|
||||
model.practiceResultProperty().set(result);
|
||||
model.setSelectedPane(practiceResult);
|
||||
} else {
|
||||
final var pane = switch (newValue.currentQuestion().questionType()) {
|
||||
case PICTURE_EXACT -> practicePictureExact;
|
||||
case PICTURE_MULTICHOICE -> practicePictureMultichoice;
|
||||
case SOUND_EXACT -> practiceSoundExact;
|
||||
case SOUND_MULTICHOICE -> practiceSoundMultichoice;
|
||||
case SOUND_MULTICHOICE_PICTURE -> practiceSoundMultichoicePicture;
|
||||
};
|
||||
model.setSelectedPane(pane);
|
||||
}
|
||||
});
|
||||
model.practiceResultProperty().addListener((_, _, newValue) -> {
|
||||
if (newValue == null) {
|
||||
model.setPracticeRun(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.practice.PracticeResult;
|
||||
import ch.gtache.fro.practice.PracticeRun;
|
||||
import ch.gtache.fro.practice.gui.PracticeModel;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
@@ -17,11 +19,15 @@ public final class FXPracticeModel implements PracticeModel {
|
||||
|
||||
private final ObservableList<Pane> panes;
|
||||
private final ObjectProperty<Pane> selectedPane;
|
||||
private final ObjectProperty<PracticeRun> practiceRun;
|
||||
private final ObjectProperty<PracticeResult> practiceResult;
|
||||
|
||||
@Inject
|
||||
FXPracticeModel() {
|
||||
this.panes = FXCollections.observableArrayList();
|
||||
this.selectedPane = new SimpleObjectProperty<>();
|
||||
this.practiceRun = new SimpleObjectProperty<>();
|
||||
this.practiceResult = new SimpleObjectProperty<>();
|
||||
}
|
||||
|
||||
ObservableList<Pane> panes() {
|
||||
@@ -39,4 +45,22 @@ public final class FXPracticeModel implements PracticeModel {
|
||||
ObjectProperty<Pane> selectedPaneProperty() {
|
||||
return selectedPane;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PracticeRun practiceRun() {
|
||||
return practiceRun.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPracticeRun(final PracticeRun practiceRun) {
|
||||
this.practiceRun.set(practiceRun);
|
||||
}
|
||||
|
||||
ObjectProperty<PracticeRun> practiceRunProperty() {
|
||||
return practiceRun;
|
||||
}
|
||||
|
||||
ObjectProperty<PracticeResult> practiceResultProperty() {
|
||||
return practiceResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Bird;
|
||||
import ch.gtache.fro.BirdTranslator;
|
||||
import ch.gtache.fro.PictureType;
|
||||
import ch.gtache.fro.practice.PracticeRunner;
|
||||
import ch.gtache.fro.practice.gui.PracticePictureExactController;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.controlsfx.control.PrefixSelectionComboBox;
|
||||
import org.controlsfx.control.textfield.CustomTextField;
|
||||
|
||||
import java.util.Objects;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* FX implementation of {@link PracticePictureExactController}
|
||||
@@ -18,25 +27,84 @@ import java.util.Objects;
|
||||
@Singleton
|
||||
public final class FXPracticePictureExactController implements PracticePictureExactController {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(FXPracticePictureExactController.class);
|
||||
|
||||
private final FXPracticePictureExactModel model;
|
||||
private final PracticeRunner runner;
|
||||
private final UserInputNormalizer normalizer;
|
||||
private final BirdTranslator translator;
|
||||
|
||||
@FXML
|
||||
private ImageView pictureView;
|
||||
@FXML
|
||||
private CustomTextField nameField;
|
||||
private CustomTextField inputField;
|
||||
@FXML
|
||||
private PrefixSelectionComboBox<PictureType> typeCombobox;
|
||||
@FXML
|
||||
private Button validateButton;
|
||||
private Button confirmButton;
|
||||
@FXML
|
||||
private Label progressLabel;
|
||||
@FXML
|
||||
private VBox guessesBox;
|
||||
@FXML
|
||||
private Label answerLabel;
|
||||
|
||||
@Inject
|
||||
FXPracticePictureExactController(final FXPracticePictureExactModel model) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
FXPracticePictureExactController(final FXPracticePictureExactModel model, final PracticeRunner runner,
|
||||
final UserInputNormalizer normalizer, final BirdTranslator translator) {
|
||||
this.model = requireNonNull(model);
|
||||
this.runner = requireNonNull(runner);
|
||||
this.normalizer = requireNonNull(normalizer);
|
||||
this.translator = requireNonNull(translator);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
inputField.textProperty().bindBidirectional(model.guessProperty());
|
||||
inputField.disableProperty().bind(model.showingResultProperty());
|
||||
confirmButton.disableProperty().bind(model.guessProperty().isEmpty().and(model.showingResultProperty().not()));
|
||||
answerLabel.visibleProperty().bind(model.showingResultProperty());
|
||||
answerLabel.textProperty().bind(model.pictureProperty().map(p -> translator.translateAsync(p.bird()).join()));
|
||||
progressLabel.textProperty().bind(model.progressProperty());
|
||||
pictureView.imageProperty().bind(model.imageProperty());
|
||||
pictureView.fitHeightProperty().bind(model.imageProperty().map(Image::getHeight));
|
||||
pictureView.fitWidthProperty().bind(model.imageProperty().map(Image::getWidth));
|
||||
model.guesses().addListener((ListChangeListener<Bird>) _ -> buildGuessesBox());
|
||||
}
|
||||
|
||||
private void buildGuessesBox() {
|
||||
final var labels = model.guesses().stream().map(this::getLabel).toList();
|
||||
guessesBox.getChildren().setAll(labels);
|
||||
}
|
||||
|
||||
private Label getLabel(final Bird bird) {
|
||||
final String text;
|
||||
final var birdName = translator.translateAsync(bird).join();
|
||||
if (bird.equals(model.picture().bird())) {
|
||||
text = "✓ " + birdName;
|
||||
} else {
|
||||
text = "✗ " + birdName;
|
||||
}
|
||||
return new Label(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void confirm() {
|
||||
throw new UnsupportedOperationException();
|
||||
final var bird = normalizer.getBird(model.guess());
|
||||
model.setGuess("");
|
||||
model.guesses().add(bird);
|
||||
if (bird.equals(model.picture().bird()) ||
|
||||
model.guessesCount() >= model.practiceRunProperty().get().parameters().guessesNumber()) {
|
||||
model.showingResultProperty().set(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void next() {
|
||||
final var newRun = runner.step(model.practiceRunProperty().get(), model.guesses().getLast());
|
||||
model.guesses().clear();
|
||||
model.showingResultProperty().set(false);
|
||||
model.practiceRunProperty().set(newRun);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,11 +114,19 @@ public final class FXPracticePictureExactController implements PracticePictureEx
|
||||
|
||||
@FXML
|
||||
private void enterPressed() {
|
||||
confirm();
|
||||
confirmOrNext();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void validatePressed() {
|
||||
confirm();
|
||||
private void confirmPressed() {
|
||||
confirmOrNext();
|
||||
}
|
||||
|
||||
private void confirmOrNext() {
|
||||
if (model.isShowingResult()) {
|
||||
next();
|
||||
} else {
|
||||
confirm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,38 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Picture;
|
||||
import ch.gtache.fro.practice.PicturePracticeQuestion;
|
||||
import ch.gtache.fro.practice.gui.PracticePictureExactModel;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.scene.image.Image;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
|
||||
/**
|
||||
* FX implementation of {@link PracticePictureExactModel}
|
||||
*/
|
||||
@Singleton
|
||||
public final class FXPracticePictureExactModel implements PracticePictureExactModel {
|
||||
public final class FXPracticePictureExactModel extends AbstractFXPraticeQuestionExactModel implements PracticePictureExactModel {
|
||||
|
||||
private final StringProperty guess;
|
||||
private final ObjectProperty<Picture> picture;
|
||||
private final ReadOnlyObjectWrapper<Picture> picture;
|
||||
private final ReadOnlyObjectWrapper<Image> image;
|
||||
|
||||
@Inject
|
||||
FXPracticePictureExactModel() {
|
||||
this.guess = new SimpleStringProperty();
|
||||
this.picture = new SimpleObjectProperty<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String guess() {
|
||||
return guess.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void guess(final String guess) {
|
||||
this.guess.set(guess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the user guess
|
||||
*
|
||||
* @return the property for the user guess
|
||||
*/
|
||||
StringProperty guessProperty() {
|
||||
return guess;
|
||||
this.picture = new ReadOnlyObjectWrapper<>();
|
||||
this.image = new ReadOnlyObjectWrapper<>();
|
||||
this.picture.bind(practiceRunProperty().map(r -> ((PicturePracticeQuestion) r.currentQuestion()).picture()));
|
||||
this.image.bind(picture.map(p -> {
|
||||
try {
|
||||
return new Image(p.path().toUri().toURL().toString(), 800, 600, true, true);
|
||||
} catch (final MalformedURLException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,16 +41,15 @@ public final class FXPracticePictureExactModel implements PracticePictureExactMo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void picture(final Picture picture) {
|
||||
this.picture.set(picture);
|
||||
public void setPicture(final Picture picture) {
|
||||
throw new IllegalArgumentException("Picture cannot be set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the picture
|
||||
*
|
||||
* @return the property for the picture
|
||||
*/
|
||||
ObjectProperty<Picture> pictureProperty() {
|
||||
return picture;
|
||||
ReadOnlyObjectProperty<Picture> pictureProperty() {
|
||||
return picture.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
ReadOnlyObjectProperty<Image> imageProperty() {
|
||||
return image.getReadOnlyProperty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.practice.PracticeRunner;
|
||||
import ch.gtache.fro.practice.gui.PracticePictureMultichoiceController;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.GridPane;
|
||||
|
||||
import java.util.Objects;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,15 +21,30 @@ import java.util.Objects;
|
||||
public final class FXPracticePictureMultichoiceController implements PracticePictureMultichoiceController {
|
||||
|
||||
private final FXPracticePictureMultichoiceModel model;
|
||||
private final PracticeRunner runner;
|
||||
|
||||
@FXML
|
||||
private ImageView pictureView;
|
||||
@FXML
|
||||
private GridPane grid;
|
||||
@FXML
|
||||
private Button validateButton;
|
||||
private Button confirmButton;
|
||||
@FXML
|
||||
private Label progressLabel;
|
||||
|
||||
@Inject
|
||||
FXPracticePictureMultichoiceController(final FXPracticePictureMultichoiceModel model) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
FXPracticePictureMultichoiceController(final FXPracticePictureMultichoiceModel model, final PracticeRunner runner) {
|
||||
this.model = requireNonNull(model);
|
||||
this.runner = requireNonNull(runner);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
confirmButton.disableProperty().bind(model.selectedProperty().isNull());
|
||||
progressLabel.textProperty().bind(model.progressProperty());
|
||||
pictureView.imageProperty().bind(model.imageProperty());
|
||||
pictureView.fitHeightProperty().bind(model.imageProperty().map(Image::getHeight));
|
||||
pictureView.fitWidthProperty().bind(model.imageProperty().map(Image::getWidth));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,11 +54,18 @@ public final class FXPracticePictureMultichoiceController implements PracticePic
|
||||
|
||||
@Override
|
||||
public void confirm() {
|
||||
final var newRun = runner.step(model.practiceRunProperty().get(), model.selected());
|
||||
model.setSelected(null);
|
||||
model.practiceRunProperty().set(newRun);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void next() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void validatePressed() {
|
||||
private void confirmPressed() {
|
||||
confirm();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,38 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Bird;
|
||||
import ch.gtache.fro.Picture;
|
||||
import ch.gtache.fro.practice.PicturePracticeQuestion;
|
||||
import ch.gtache.fro.practice.gui.PracticePictureMultichoiceModel;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.scene.image.Image;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
|
||||
/**
|
||||
* FX implementation of {@link PracticePictureMultichoiceModel}
|
||||
*/
|
||||
@Singleton
|
||||
public final class FXPracticePictureMultichoiceModel implements PracticePictureMultichoiceModel {
|
||||
public final class FXPracticePictureMultichoiceModel extends AbstractFXPraticeQuestionMultichoiceModel implements PracticePictureMultichoiceModel {
|
||||
|
||||
private final ObjectProperty<Picture> picture;
|
||||
private final ObservableList<Bird> suggestions;
|
||||
private final ObjectProperty<Bird> selected;
|
||||
private final ReadOnlyObjectWrapper<Picture> picture;
|
||||
private final ReadOnlyObjectWrapper<Image> image;
|
||||
|
||||
@Inject
|
||||
FXPracticePictureMultichoiceModel() {
|
||||
this.picture = new SimpleObjectProperty<>();
|
||||
this.suggestions = FXCollections.observableArrayList();
|
||||
this.selected = new SimpleObjectProperty<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<Bird> suggestions() {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bird selected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(final Bird selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the selected bird
|
||||
*
|
||||
* @return the property for the selected bird
|
||||
*/
|
||||
ObjectProperty<Bird> selectedProperty() {
|
||||
return selected;
|
||||
this.picture = new ReadOnlyObjectWrapper<>();
|
||||
this.image = new ReadOnlyObjectWrapper<>();
|
||||
this.picture.bind(practiceRunProperty().map(r -> ((PicturePracticeQuestion) r.currentQuestion()).picture()));
|
||||
this.image.bind(picture.map(p -> {
|
||||
try {
|
||||
return new Image(p.path().toUri().toURL().toString(), 800, 600, true, true);
|
||||
} catch (final MalformedURLException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,16 +41,15 @@ public final class FXPracticePictureMultichoiceModel implements PracticePictureM
|
||||
}
|
||||
|
||||
@Override
|
||||
public void picture(final Picture picture) {
|
||||
this.picture.set(picture);
|
||||
public void setPicture(final Picture picture) {
|
||||
throw new IllegalArgumentException("Picture cannot be set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the picture
|
||||
*
|
||||
* @return the property for the picture
|
||||
*/
|
||||
ObjectProperty<Picture> pictureProperty() {
|
||||
return picture;
|
||||
ReadOnlyObjectProperty<Picture> pictureProperty() {
|
||||
return picture.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
ReadOnlyObjectProperty<Image> imageProperty() {
|
||||
return image.getReadOnlyProperty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import ch.gtache.fro.practice.gui.PracticeResultController;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -23,12 +24,32 @@ public final class FXPracticeResultController implements PracticeResultControlle
|
||||
private Label successListLabel;
|
||||
@FXML
|
||||
private Label failureListLabel;
|
||||
@FXML
|
||||
private Button confirmButton;
|
||||
|
||||
@Inject
|
||||
FXPracticeResultController(final FXPracticeResultModel model) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
successNumberLabel.textProperty().bind(model.successLabelProperty());
|
||||
failureNumberLabel.textProperty().bind(model.failureLabelProperty());
|
||||
successListLabel.textProperty().bind(model.successListLabelProperty());
|
||||
failureListLabel.textProperty().bind(model.failureListLabelProperty());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void confirmPressed() {
|
||||
confirm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void confirm() {
|
||||
model.setResult(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FXPracticeResultModel model() {
|
||||
return model;
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.BirdTranslator;
|
||||
import ch.gtache.fro.practice.PracticeResult;
|
||||
import ch.gtache.fro.practice.gui.PracticeResultModel;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyStringProperty;
|
||||
import javafx.beans.property.ReadOnlyStringWrapper;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* FX implementation of {@link PracticeResultModel}
|
||||
@@ -10,9 +18,72 @@ import jakarta.inject.Singleton;
|
||||
@Singleton
|
||||
public final class FXPracticeResultModel implements PracticeResultModel {
|
||||
|
||||
private final ObjectProperty<PracticeResult> practiceResult;
|
||||
private final ReadOnlyStringWrapper successLabel;
|
||||
private final ReadOnlyStringWrapper failureLabel;
|
||||
private final ReadOnlyStringWrapper successListLabel;
|
||||
private final ReadOnlyStringWrapper failureListLabel;
|
||||
|
||||
@Inject
|
||||
FXPracticeResultModel() {
|
||||
|
||||
FXPracticeResultModel(final BirdTranslator translator) {
|
||||
this.practiceResult = new SimpleObjectProperty<>();
|
||||
this.successLabel = new ReadOnlyStringWrapper();
|
||||
this.failureLabel = new ReadOnlyStringWrapper();
|
||||
this.successListLabel = new ReadOnlyStringWrapper();
|
||||
this.failureListLabel = new ReadOnlyStringWrapper();
|
||||
successLabel.bind(resultProperty().map(r -> r.correctQuestionsCount() + "/" + r.totalQuestions()));
|
||||
failureLabel.bind(resultProperty().map(r -> r.failedQuestionsCount() + "/" + r.totalQuestions()));
|
||||
successListLabel.bind(resultProperty().map(r -> r.correctQuestions().stream().map(pq -> translator.translateAsync(pq.bird()).join()).collect(Collectors.joining(", "))));
|
||||
failureListLabel.bind(resultProperty().map(r -> r.failedQuestions().stream().map(pq -> translator.translateAsync(pq.bird()).join()).collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PracticeResult result() {
|
||||
return practiceResult.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(final PracticeResult result) {
|
||||
this.practiceResult.set(result);
|
||||
}
|
||||
|
||||
ObjectProperty<PracticeResult> resultProperty() {
|
||||
return practiceResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String successLabel() {
|
||||
return successLabel.get();
|
||||
}
|
||||
|
||||
ReadOnlyStringProperty successLabelProperty() {
|
||||
return successLabel.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String failureLabel() {
|
||||
return failureLabel.get();
|
||||
}
|
||||
|
||||
ReadOnlyStringProperty failureLabelProperty() {
|
||||
return failureLabel.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String successListLabel() {
|
||||
return successListLabel.get();
|
||||
}
|
||||
|
||||
ReadOnlyStringProperty successListLabelProperty() {
|
||||
return successListLabel.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String failureListLabel() {
|
||||
return failureListLabel.get();
|
||||
}
|
||||
|
||||
ReadOnlyStringProperty failureListLabelProperty() {
|
||||
return failureListLabel.getReadOnlyProperty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.practice.PracticeType;
|
||||
import ch.gtache.fro.practice.PracticeRunner;
|
||||
import ch.gtache.fro.practice.QuestionType;
|
||||
import ch.gtache.fro.practice.gui.PracticeSettingsController;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Spinner;
|
||||
@@ -19,34 +21,53 @@ import static java.util.Objects.requireNonNull;
|
||||
public final class FXPracticeSettingsController implements PracticeSettingsController {
|
||||
|
||||
private final FXPracticeSettingsModel model;
|
||||
private final PracticeTypeConverter practiceTypeConverter;
|
||||
private final QuestionTypeConverter questionTypeConverter;
|
||||
private final PracticeRunner runner;
|
||||
@FXML
|
||||
private PrefixSelectionComboBox<PracticeType> practiceTypeCombobox;
|
||||
private PrefixSelectionComboBox<QuestionType> questionTypeCombobox;
|
||||
@FXML
|
||||
private Spinner<Integer> questionsNumberSpinner;
|
||||
@FXML
|
||||
private Label suggestionsNumberLabel;
|
||||
@FXML
|
||||
private Spinner<Integer> suggestionsNumberSpinner;
|
||||
@FXML
|
||||
private Spinner<Integer> guessesNumberSpinner;
|
||||
|
||||
private final ObjectProperty<Integer> guessesNumberProperty;
|
||||
private final ObjectProperty<Integer> questionsNumberProperty;
|
||||
private final ObjectProperty<Integer> suggestionsNumberProperty;
|
||||
|
||||
@Inject
|
||||
FXPracticeSettingsController(final FXPracticeSettingsModel model, final PracticeTypeConverter practiceTypeConverter) {
|
||||
FXPracticeSettingsController(final FXPracticeSettingsModel model, final QuestionTypeConverter questionTypeConverter,
|
||||
final PracticeRunner runner) {
|
||||
this.model = requireNonNull(model);
|
||||
this.practiceTypeConverter = requireNonNull(practiceTypeConverter);
|
||||
this.questionTypeConverter = requireNonNull(questionTypeConverter);
|
||||
this.runner = requireNonNull(runner);
|
||||
this.guessesNumberProperty = model.guessesNumberProperty().asObject();
|
||||
this.questionsNumberProperty = model.questionsNumberProperty().asObject();
|
||||
this.suggestionsNumberProperty = model.suggestionsNumberProperty().asObject();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
practiceTypeCombobox.setItems(model.practiceTypes());
|
||||
practiceTypeCombobox.valueProperty().bindBidirectional(model.practiceTypeProperty());
|
||||
practiceTypeCombobox.setConverter(practiceTypeConverter);
|
||||
questionTypeCombobox.setItems(model.questionTypes());
|
||||
questionTypeCombobox.valueProperty().bindBidirectional(model.questionTypeProperty());
|
||||
questionTypeCombobox.setConverter(questionTypeConverter);
|
||||
guessesNumberSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, Integer.MAX_VALUE, model.guessesNumber()));
|
||||
guessesNumberSpinner.getValueFactory().valueProperty().bindBidirectional(guessesNumberProperty);
|
||||
questionsNumberSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, Integer.MAX_VALUE, model.suggestionsNumber()));
|
||||
questionsNumberSpinner.getValueFactory().valueProperty().bindBidirectional(questionsNumberProperty);
|
||||
suggestionsNumberLabel.visibleProperty().bind(model.hasSuggestionsProperty());
|
||||
suggestionsNumberSpinner.visibleProperty().bind(model.hasSuggestionsProperty());
|
||||
suggestionsNumberSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 8, model.suggestionsNumber()));
|
||||
suggestionsNumberSpinner.getValueFactory().valueProperty().bindBidirectional(model.suggestionsNumberProperty().asObject());
|
||||
suggestionsNumberSpinner.getValueFactory().valueProperty().bindBidirectional(suggestionsNumberProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPractice() {
|
||||
throw new UnsupportedOperationException();
|
||||
final var run = runner.start(model.practiceParameters());
|
||||
model.practiceRunProperty().set(run);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -1,53 +1,88 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.practice.PracticeType;
|
||||
import ch.gtache.fro.practice.PracticeConfiguration;
|
||||
import ch.gtache.fro.practice.PracticeParameters;
|
||||
import ch.gtache.fro.practice.PracticeRun;
|
||||
import ch.gtache.fro.practice.QuestionType;
|
||||
import ch.gtache.fro.practice.gui.PracticeSettingsModel;
|
||||
import ch.gtache.fro.practice.impl.PracticeParametersImpl;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* FX implementation of {@link PracticeSettingsModel}
|
||||
*/
|
||||
@Singleton
|
||||
public final class FXPracticeSettingsModel implements PracticeSettingsModel {
|
||||
|
||||
private static final int DEFAULT_QUESTIONS_NUMBER = 20;
|
||||
private static final int DEFAULT_SUGGESTIONS_NUMBER = 4;
|
||||
|
||||
private final ObservableList<PracticeType> practiceTypes;
|
||||
private final ObjectProperty<PracticeType> practiceType;
|
||||
private final ObservableList<QuestionType> questionTypes;
|
||||
private final ObjectProperty<QuestionType> questionType;
|
||||
private final ReadOnlyBooleanWrapper hasSuggestions;
|
||||
private final IntegerProperty guessesNumber;
|
||||
private final IntegerProperty questionsNumber;
|
||||
private final IntegerProperty suggestionsNumber;
|
||||
private final ReadOnlyObjectWrapper<PracticeParameters> practiceParameters;
|
||||
private final ObjectProperty<PracticeRun> practiceRun;
|
||||
|
||||
@Inject
|
||||
FXPracticeSettingsModel() {
|
||||
this.practiceTypes = FXCollections.observableArrayList(PracticeType.values());
|
||||
this.practiceType = new SimpleObjectProperty<>(PracticeType.PICTURE_EXACT);
|
||||
FXPracticeSettingsModel(final PracticeConfiguration configuration) {
|
||||
this.questionTypes = FXCollections.observableArrayList(QuestionType.values());
|
||||
this.questionType = new SimpleObjectProperty<>(QuestionType.PICTURE_EXACT);
|
||||
this.hasSuggestions = new ReadOnlyBooleanWrapper(true);
|
||||
hasSuggestions.bind(practiceType.isEqualTo(PracticeType.PICTURE_EXACT).or(practiceType.isEqualTo(PracticeType.SOUND_EXACT)));
|
||||
this.guessesNumber = new SimpleIntegerProperty(configuration.guessesNumber());
|
||||
this.questionsNumber = new SimpleIntegerProperty(DEFAULT_QUESTIONS_NUMBER);
|
||||
this.suggestionsNumber = new SimpleIntegerProperty(DEFAULT_SUGGESTIONS_NUMBER);
|
||||
this.practiceParameters = new ReadOnlyObjectWrapper<>();
|
||||
this.practiceRun = new SimpleObjectProperty<>();
|
||||
practiceParameters.bind(Bindings.createObjectBinding(() -> new PracticeParametersImpl(configuration.birdPracticeParameters(), Map.of(questionType(), 1.0),
|
||||
guessesNumber(), questionsNumber(), suggestionsNumber()), questionType, guessesNumber, questionsNumber, suggestionsNumber));
|
||||
hasSuggestions.bind(questionType.isNotEqualTo(QuestionType.PICTURE_EXACT).and(questionType.isNotEqualTo(QuestionType.SOUND_EXACT)));
|
||||
guessesNumber.addListener((_, _, newValue) -> configuration.setGuessesNumber(newValue.intValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<PracticeType> practiceTypes() {
|
||||
return practiceTypes;
|
||||
public ObservableList<QuestionType> questionTypes() {
|
||||
return questionTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PracticeType practiceType() {
|
||||
return practiceType.get();
|
||||
public QuestionType questionType() {
|
||||
return questionType.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void practiceType(final PracticeType practiceType) {
|
||||
this.practiceType.set(practiceType);
|
||||
public void setQuestionType(final QuestionType questionType) {
|
||||
this.questionType.set(questionType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int guessesNumber() {
|
||||
return guessesNumber.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGuessesNumber(final int guessesNumber) {
|
||||
this.guessesNumber.set(guessesNumber);
|
||||
}
|
||||
|
||||
IntegerProperty guessesNumberProperty() {
|
||||
return guessesNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,8 +90,8 @@ public final class FXPracticeSettingsModel implements PracticeSettingsModel {
|
||||
*
|
||||
* @return The practice type property
|
||||
*/
|
||||
ObjectProperty<PracticeType> practiceTypeProperty() {
|
||||
return practiceType;
|
||||
ObjectProperty<QuestionType> questionTypeProperty() {
|
||||
return questionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,16 +108,39 @@ public final class FXPracticeSettingsModel implements PracticeSettingsModel {
|
||||
return hasSuggestions.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int questionsNumber() {
|
||||
return questionsNumber.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuestionsNumber(final int questionsNumber) {
|
||||
this.questionsNumber.set(questionsNumber);
|
||||
}
|
||||
|
||||
IntegerProperty questionsNumberProperty() {
|
||||
return questionsNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int suggestionsNumber() {
|
||||
return suggestionsNumber.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestionsNumber(final int suggestionsNumber) {
|
||||
public void setSuggestionsNumber(final int suggestionsNumber) {
|
||||
this.suggestionsNumber.set(suggestionsNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PracticeParameters practiceParameters() {
|
||||
return practiceParameters.get();
|
||||
}
|
||||
|
||||
ReadOnlyObjectProperty<PracticeParameters> practiceParametersProperty() {
|
||||
return practiceParameters.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the suggestions number property
|
||||
*
|
||||
@@ -91,4 +149,8 @@ public final class FXPracticeSettingsModel implements PracticeSettingsModel {
|
||||
IntegerProperty suggestionsNumberProperty() {
|
||||
return suggestionsNumber;
|
||||
}
|
||||
|
||||
ObjectProperty<PracticeRun> practiceRunProperty() {
|
||||
return practiceRun;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.SoundType;
|
||||
import ch.gtache.fro.practice.PracticeRunner;
|
||||
import ch.gtache.fro.practice.gui.PracticeSoundExactController;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.media.MediaView;
|
||||
import org.controlsfx.control.PrefixSelectionComboBox;
|
||||
|
||||
import java.util.Objects;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,19 +23,35 @@ import java.util.Objects;
|
||||
public final class FXPracticeSoundExactController implements PracticeSoundExactController {
|
||||
|
||||
private final FXPracticeSoundExactModel model;
|
||||
private final UserInputNormalizer normalizer;
|
||||
private final PracticeRunner runner;
|
||||
|
||||
@FXML
|
||||
private Button validateButton;
|
||||
private Button confirmButton;
|
||||
@FXML
|
||||
private TextField inputField;
|
||||
@FXML
|
||||
private MediaView mediaView;
|
||||
@FXML
|
||||
private PrefixSelectionComboBox<SoundType> typeCombobox;
|
||||
@FXML
|
||||
private Label progressLabel;
|
||||
@FXML
|
||||
private VBox guessesBox;
|
||||
|
||||
@Inject
|
||||
FXPracticeSoundExactController(final FXPracticeSoundExactModel model) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
FXPracticeSoundExactController(final FXPracticeSoundExactModel model, final PracticeRunner runner,
|
||||
final UserInputNormalizer normalizer) {
|
||||
this.model = requireNonNull(model);
|
||||
this.normalizer = requireNonNull(normalizer);
|
||||
this.runner = requireNonNull(runner);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
inputField.textProperty().bindBidirectional(model.guessProperty());
|
||||
confirmButton.disableProperty().bind(model.guessProperty().isEmpty());
|
||||
progressLabel.textProperty().bind(model.progressProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,11 +61,19 @@ public final class FXPracticeSoundExactController implements PracticeSoundExactC
|
||||
|
||||
@Override
|
||||
public void confirm() {
|
||||
final var bird = normalizer.getBird(model.guess());
|
||||
model.setGuess("");
|
||||
final var newRun = runner.step(model.practiceRunProperty().get(), bird);
|
||||
model.practiceRunProperty().set(newRun);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void next() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void validatePressed() {
|
||||
private void confirmButton() {
|
||||
confirm();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +1,26 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Sound;
|
||||
import ch.gtache.fro.practice.SoundPracticeQuestion;
|
||||
import ch.gtache.fro.practice.gui.PracticeSoundExactModel;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
|
||||
|
||||
/**
|
||||
* FX implementation of {@link PracticeSoundExactModel}
|
||||
*/
|
||||
@Singleton
|
||||
public final class FXPracticeSoundExactModel implements PracticeSoundExactModel {
|
||||
public final class FXPracticeSoundExactModel extends AbstractFXPraticeQuestionExactModel implements PracticeSoundExactModel {
|
||||
|
||||
private final StringProperty guess;
|
||||
private final ObjectProperty<Sound> sound;
|
||||
private final ReadOnlyObjectWrapper<Sound> sound;
|
||||
|
||||
@Inject
|
||||
FXPracticeSoundExactModel() {
|
||||
this.guess = new SimpleStringProperty();
|
||||
this.sound = new SimpleObjectProperty<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String guess() {
|
||||
return guess.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void guess(final String guess) {
|
||||
this.guess.set(guess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the user guess
|
||||
*
|
||||
* @return the property for the user guess
|
||||
*/
|
||||
StringProperty guessProperty() {
|
||||
return guess;
|
||||
this.sound = new ReadOnlyObjectWrapper<>();
|
||||
this.sound.bind(practiceRunProperty().map(r -> ((SoundPracticeQuestion) r.currentQuestion()).sound()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,16 +29,11 @@ public final class FXPracticeSoundExactModel implements PracticeSoundExactModel
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sound(final Sound sound) {
|
||||
this.sound.set(sound);
|
||||
public void setSound(final Sound sound) {
|
||||
throw new IllegalArgumentException("Sound cannot be set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the sound
|
||||
*
|
||||
* @return the property for the sound
|
||||
*/
|
||||
ObjectProperty<Sound> soundProperty() {
|
||||
return sound;
|
||||
ReadOnlyObjectProperty<Sound> soundProperty() {
|
||||
return sound.getReadOnlyProperty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.practice.PracticeRunner;
|
||||
import ch.gtache.fro.practice.gui.PracticeSoundMultichoiceController;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.media.MediaView;
|
||||
|
||||
import java.util.Objects;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,17 +20,27 @@ import java.util.Objects;
|
||||
public final class FXPracticeSoundMultichoiceController implements PracticeSoundMultichoiceController {
|
||||
|
||||
private final FXPracticeSoundMultichoiceModel model;
|
||||
private final PracticeRunner runner;
|
||||
|
||||
@FXML
|
||||
private GridPane grid;
|
||||
@FXML
|
||||
private MediaView mediaView;
|
||||
@FXML
|
||||
private Button validateButton;
|
||||
private Button confirmButton;
|
||||
@FXML
|
||||
private Label progressLabel;
|
||||
|
||||
@Inject
|
||||
FXPracticeSoundMultichoiceController(final FXPracticeSoundMultichoiceModel model) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
FXPracticeSoundMultichoiceController(final FXPracticeSoundMultichoiceModel model, final PracticeRunner runner) {
|
||||
this.model = requireNonNull(model);
|
||||
this.runner = requireNonNull(runner);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
confirmButton.disableProperty().bind(model.selectedProperty().isNull());
|
||||
progressLabel.textProperty().bind(model.progressProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,11 +50,18 @@ public final class FXPracticeSoundMultichoiceController implements PracticeSound
|
||||
|
||||
@Override
|
||||
public void confirm() {
|
||||
final var newRun = runner.step(model.practiceRunProperty().get(), model.selected());
|
||||
model.setSelected(null);
|
||||
model.practiceRunProperty().set(newRun);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void next() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void validatePressed() {
|
||||
private void confirmPressed() {
|
||||
confirm();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,26 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Bird;
|
||||
import ch.gtache.fro.Sound;
|
||||
import ch.gtache.fro.practice.SoundPracticeQuestion;
|
||||
import ch.gtache.fro.practice.gui.PracticeSoundMultichoiceModel;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
|
||||
|
||||
/**
|
||||
* FX implementation of {@link PracticeSoundMultichoiceModel}
|
||||
*/
|
||||
@Singleton
|
||||
public final class FXPracticeSoundMultichoiceModel implements PracticeSoundMultichoiceModel {
|
||||
public final class FXPracticeSoundMultichoiceModel extends AbstractFXPraticeQuestionMultichoiceModel implements PracticeSoundMultichoiceModel {
|
||||
|
||||
private final ObservableList<Bird> suggestions;
|
||||
private final ObjectProperty<Bird> selected;
|
||||
private final ObjectProperty<Sound> sound;
|
||||
private final ReadOnlyObjectWrapper<Sound> sound;
|
||||
|
||||
@Inject
|
||||
FXPracticeSoundMultichoiceModel() {
|
||||
this.suggestions = FXCollections.observableArrayList();
|
||||
this.selected = new SimpleObjectProperty<>();
|
||||
this.sound = new SimpleObjectProperty<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<Bird> suggestions() {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bird selected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(final Bird selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the selected bird
|
||||
*
|
||||
* @return the property for the selected bird
|
||||
*/
|
||||
ObjectProperty<Bird> selectedProperty() {
|
||||
return selected;
|
||||
this.sound = new ReadOnlyObjectWrapper<>();
|
||||
this.sound.bind(practiceRunProperty().map(r -> ((SoundPracticeQuestion) r.currentQuestion()).sound()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,16 +29,11 @@ public final class FXPracticeSoundMultichoiceModel implements PracticeSoundMulti
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sound(final Sound sound) {
|
||||
this.sound.set(sound);
|
||||
public void setSound(final Sound sound) {
|
||||
throw new IllegalArgumentException("Sound cannot be set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the sound
|
||||
*
|
||||
* @return the property for the sound
|
||||
*/
|
||||
ObjectProperty<Sound> soundProperty() {
|
||||
return sound;
|
||||
ReadOnlyObjectProperty<Sound> soundProperty() {
|
||||
return sound.getReadOnlyProperty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.practice.PracticeRunner;
|
||||
import ch.gtache.fro.practice.gui.PracticeSoundMultichoicePictureController;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.media.MediaView;
|
||||
|
||||
import java.util.Objects;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,17 +20,28 @@ import java.util.Objects;
|
||||
public final class FXPracticeSoundMultichoicePictureController implements PracticeSoundMultichoicePictureController {
|
||||
|
||||
private final FXPracticeSoundMultichoicePictureModel model;
|
||||
private final PracticeRunner runner;
|
||||
|
||||
@FXML
|
||||
private GridPane grid;
|
||||
@FXML
|
||||
private MediaView mediaView;
|
||||
@FXML
|
||||
private Button validateButton;
|
||||
private Button confirmButton;
|
||||
@FXML
|
||||
private Label progressLabel;
|
||||
|
||||
@Inject
|
||||
FXPracticeSoundMultichoicePictureController(final FXPracticeSoundMultichoicePictureModel model) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
FXPracticeSoundMultichoicePictureController(final FXPracticeSoundMultichoicePictureModel model,
|
||||
final PracticeRunner runner) {
|
||||
this.model = requireNonNull(model);
|
||||
this.runner = requireNonNull(runner);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
confirmButton.disableProperty().bind(model.selectedProperty().isNull());
|
||||
progressLabel.textProperty().bind(model.progressProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,11 +51,18 @@ public final class FXPracticeSoundMultichoicePictureController implements Practi
|
||||
|
||||
@Override
|
||||
public void confirm() {
|
||||
final var newRun = runner.step(model.practiceRunProperty().get(), model.selected().bird());
|
||||
model.setSelected(null);
|
||||
model.practiceRunProperty().set(newRun);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void next() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void validatePressed() {
|
||||
private void confirmPressed() {
|
||||
confirm();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Picture;
|
||||
import ch.gtache.fro.Sound;
|
||||
import ch.gtache.fro.practice.SoundPracticeQuestion;
|
||||
import ch.gtache.fro.practice.gui.PracticeSoundMultichoicePictureModel;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -15,17 +18,18 @@ import javafx.collections.ObservableList;
|
||||
* FX implementation of {@link PracticeSoundMultichoicePictureModel}
|
||||
*/
|
||||
@Singleton
|
||||
public final class FXPracticeSoundMultichoicePictureModel implements PracticeSoundMultichoicePictureModel {
|
||||
public final class FXPracticeSoundMultichoicePictureModel extends AbstractFXPracticeQuestionModel implements PracticeSoundMultichoicePictureModel {
|
||||
|
||||
private final ObservableList<Picture> suggestions;
|
||||
private final ObjectProperty<Picture> selected;
|
||||
private final ObjectProperty<Sound> sound;
|
||||
private final ReadOnlyObjectWrapper<Sound> sound;
|
||||
|
||||
@Inject
|
||||
FXPracticeSoundMultichoicePictureModel() {
|
||||
this.suggestions = FXCollections.observableArrayList();
|
||||
this.selected = new SimpleObjectProperty<>();
|
||||
this.sound = new SimpleObjectProperty<>();
|
||||
this.sound = new ReadOnlyObjectWrapper<>();
|
||||
this.sound.bind(practiceRunProperty().map(r -> ((SoundPracticeQuestion) r.currentQuestion()).sound()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,15 +43,10 @@ public final class FXPracticeSoundMultichoicePictureModel implements PracticeSou
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(final Picture selected) {
|
||||
public void setSelected(final Picture selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the selected picture
|
||||
*
|
||||
* @return the property for the selected picture
|
||||
*/
|
||||
ObjectProperty<Picture> selectedProperty() {
|
||||
return selected;
|
||||
}
|
||||
@@ -58,16 +57,11 @@ public final class FXPracticeSoundMultichoicePictureModel implements PracticeSou
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sound(final Sound sound) {
|
||||
this.sound.set(sound);
|
||||
public void setSound(final Sound sound) {
|
||||
throw new IllegalArgumentException("Sound cannot be set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property for the sound
|
||||
*
|
||||
* @return the property for the sound
|
||||
*/
|
||||
ObjectProperty<Sound> soundProperty() {
|
||||
return sound;
|
||||
ReadOnlyObjectProperty<Sound> soundProperty() {
|
||||
return sound.getReadOnlyProperty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.TranslationException;
|
||||
import ch.gtache.fro.practice.PracticeType;
|
||||
import ch.gtache.fro.practice.PracticeTypeTranslator;
|
||||
import jakarta.inject.Inject;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class PracticeTypeConverter extends StringConverter<PracticeType> {
|
||||
|
||||
private final PracticeTypeTranslator translator;
|
||||
|
||||
@Inject
|
||||
PracticeTypeConverter(final PracticeTypeTranslator translator) {
|
||||
this.translator = Objects.requireNonNull(translator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(final PracticeType object) {
|
||||
try {
|
||||
return translator.translate(object);
|
||||
} catch (final TranslationException e) {
|
||||
return object.name();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PracticeType fromString(final String string) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.TranslationException;
|
||||
import ch.gtache.fro.practice.QuestionType;
|
||||
import ch.gtache.fro.practice.QuestionTypeTranslator;
|
||||
import jakarta.inject.Inject;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class QuestionTypeConverter extends StringConverter<QuestionType> {
|
||||
|
||||
private final QuestionTypeTranslator translator;
|
||||
|
||||
@Inject
|
||||
QuestionTypeConverter(final QuestionTypeTranslator translator) {
|
||||
this.translator = Objects.requireNonNull(translator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(final QuestionType object) {
|
||||
try {
|
||||
return translator.translate(object);
|
||||
} catch (final TranslationException _) {
|
||||
return object.name();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuestionType fromString(final String string) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
import ch.gtache.fro.Bird;
|
||||
import ch.gtache.fro.BirdProvider;
|
||||
import ch.gtache.fro.BirdTranslator;
|
||||
import ch.gtache.fro.ProvisionException;
|
||||
import ch.gtache.fro.TranslationException;
|
||||
import ch.gtache.fro.impl.BirdImpl;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
|
||||
import java.text.Normalizer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Normalizes user input to match bird names
|
||||
*/
|
||||
@Singleton
|
||||
public class UserInputNormalizer {
|
||||
|
||||
private static final Pattern DIACRITICS_PATTERN = Pattern.compile("\\p{M}");
|
||||
|
||||
private final Map<String, Bird> birdMap;
|
||||
|
||||
@Inject
|
||||
UserInputNormalizer(final BirdProvider provider, final BirdTranslator translator) {
|
||||
this.birdMap = new HashMap<>();
|
||||
try {
|
||||
for (final var object : provider.getAllObjects()) {
|
||||
birdMap.put(normalize(object.name()), object);
|
||||
birdMap.put(normalize(translator.translate(object)), object);
|
||||
}
|
||||
} catch (final ProvisionException e) {
|
||||
throw new IllegalStateException("Error getting all birds", e);
|
||||
} catch (final TranslationException e) {
|
||||
throw new IllegalStateException("Error translating bird name", e);
|
||||
}
|
||||
}
|
||||
|
||||
Bird getBird(final String input) {
|
||||
final var normalized = normalize(input);
|
||||
return birdMap.getOrDefault(normalized, new BirdImpl(input));
|
||||
}
|
||||
|
||||
private static String normalize(final CharSequence input) {
|
||||
return DIACRITICS_PATTERN.matcher(Normalizer.normalize(input, Normalizer.Form.NFKD)).replaceAll("").toUpperCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,18 @@
|
||||
*/
|
||||
module ch.gtache.fro.gui.fx {
|
||||
requires transitive ch.gtache.fro.gui.api;
|
||||
requires ch.gtache.fro.gui.core;
|
||||
requires dagger;
|
||||
requires jakarta.inject;
|
||||
requires javafx.fxml;
|
||||
requires transitive ch.gtache.fro.gui.core;
|
||||
requires transitive javafx.fxml;
|
||||
requires javafx.graphics;
|
||||
requires javafx.media;
|
||||
requires org.controlsfx.controls;
|
||||
requires javafx.base;
|
||||
requires ch.gtache.fro.api;
|
||||
requires org.apache.logging.log4j;
|
||||
requires javafx.controls;
|
||||
|
||||
exports ch.gtache.fro.gui.fx;
|
||||
exports ch.gtache.fro.practice.gui.fx;
|
||||
|
||||
exports ch.gtache.fro.modules.gui.fx;
|
||||
exports ch.gtache.fro.modules.practice.gui.fx;
|
||||
|
||||
|
||||
@@ -2,32 +2,38 @@
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import org.controlsfx.control.PrefixSelectionComboBox?>
|
||||
<?import org.controlsfx.control.textfield.CustomTextField?>
|
||||
<GridPane hgap="10.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" vgap="10.0"
|
||||
xmlns="http://javafx.com/javafx/24.0.1" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticePictureExactController">
|
||||
|
||||
<GridPane hgap="10.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" vgap="10.0" xmlns="http://javafx.com/javafx/24.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticePictureExactController">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="ALWAYS"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="ALWAYS" />
|
||||
<RowConstraints vgrow="ALWAYS" />
|
||||
<RowConstraints vgrow="ALWAYS" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<ImageView fx:id="pictureView" fitHeight="300.0" fitWidth="300.0" pickOnBounds="true" preserveRatio="true"
|
||||
GridPane.columnSpan="2147483647"/>
|
||||
<CustomTextField fx:id="nameField" onAction="#enterPressed" GridPane.rowIndex="1"/>
|
||||
<PrefixSelectionComboBox fx:id="typeCombobox" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
<Button fx:id="validateButton" mnemonicParsing="false" onAction="#validatePressed"
|
||||
text="%practice.picture.exact.validate.button.label" GridPane.columnSpan="2147483647"
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="2"/>
|
||||
<ImageView fx:id="pictureView" fitHeight="800.0" fitWidth="800.0" pickOnBounds="true" preserveRatio="true" GridPane.columnSpan="2147483647" GridPane.rowIndex="1" />
|
||||
<CustomTextField fx:id="inputField" onAction="#enterPressed" GridPane.rowIndex="3" />
|
||||
<PrefixSelectionComboBox fx:id="typeCombobox" managed="false" visible="false" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||
<Button fx:id="confirmButton" mnemonicParsing="false" onAction="#confirmPressed" text="%practice.picture.exact.validate.button.label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="5" />
|
||||
<Label fx:id="progressLabel" text="Label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" />
|
||||
<VBox fx:id="guessesBox" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="4" />
|
||||
<Label fx:id="answerLabel" GridPane.rowIndex="2" />
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
</GridPane>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane fx:id="grid" hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/24.0.1"
|
||||
@@ -14,13 +15,15 @@
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<ImageView fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true"
|
||||
GridPane.columnSpan="2147483647" GridPane.halignment="CENTER"/>
|
||||
<Button fx:id="validateButton" mnemonicParsing="false" onAction="#validatePressed"
|
||||
<ImageView fx:id="pictureView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true"
|
||||
GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1"/>
|
||||
<Button fx:id="confirmButton" mnemonicParsing="false" onAction="#confirmPressed"
|
||||
text="%practice.picture.multichoice.validate.button.label" GridPane.columnSpan="2147483647"
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="1"/>
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="2"/>
|
||||
<Label fx:id="progressLabel" text="Label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER"/>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane hgap="10.0" vgap="10.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/24.0.1"
|
||||
@@ -14,6 +15,7 @@
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="%practice.result.label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER"/>
|
||||
@@ -23,6 +25,9 @@
|
||||
<Label fx:id="failureNumberLabel" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
<Label fx:id="successListLabel" GridPane.columnIndex="2" GridPane.rowIndex="1"/>
|
||||
<Label fx:id="failureListLabel" GridPane.columnIndex="2" GridPane.rowIndex="2"/>
|
||||
<Button fx:id="confirmButton" mnemonicParsing="false" onAction="#confirmPressed"
|
||||
text="%practice.result.confirm.button.label" GridPane.columnSpan="2147483647"
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="3"/>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
|
||||
@@ -4,28 +4,35 @@
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.Spinner?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import org.controlsfx.control.PrefixSelectionComboBox?>
|
||||
<GridPane hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/24.0.1" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticeSettingsController">
|
||||
|
||||
<GridPane hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/24.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticeSettingsController">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="%practice.settings.type.label"/>
|
||||
<PrefixSelectionComboBox fx:id="practiceTypeCombobox" GridPane.columnIndex="1"/>
|
||||
<Label fx:id="suggestionsNumberLabel" text="%practice.settings.suggestions.number.label" GridPane.rowIndex="1"/>
|
||||
<Spinner fx:id="suggestionsNumberSpinner" editable="true" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
<Button mnemonicParsing="false" onAction="#startPressed" text="%practice.settings.start.button.label"
|
||||
GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2"/>
|
||||
<Label text="%practice.settings.type.label" />
|
||||
<PrefixSelectionComboBox fx:id="questionTypeCombobox" GridPane.columnIndex="1" />
|
||||
<Label fx:id="suggestionsNumberLabel" text="%practice.settings.suggestions.number.label" GridPane.rowIndex="3" />
|
||||
<Spinner fx:id="suggestionsNumberSpinner" editable="true" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||
<Button mnemonicParsing="false" onAction="#startPressed" text="%practice.settings.start.button.label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="4" />
|
||||
<Label text="%practice.settings.questions.number.label" GridPane.rowIndex="1" />
|
||||
<Spinner fx:id="questionsNumberSpinner" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||
<Label text="%practice.settings.guesses.number.label" GridPane.rowIndex="2" />
|
||||
<Spinner fx:id="guessesNumberSpinner" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
</GridPane>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.media.MediaView?>
|
||||
@@ -16,15 +17,20 @@
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Button fx:id="validateButton" mnemonicParsing="false" onAction="#validatePressed"
|
||||
<Button fx:id="confirmButton" mnemonicParsing="false" onAction="#confirmButton"
|
||||
text="%practice.sound.exact.validate.button.label" GridPane.columnSpan="2147483647"
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="2"/>
|
||||
<TextField fx:id="inputField" onAction="#enterPressed" GridPane.rowIndex="1"/>
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="4"/>
|
||||
<TextField fx:id="inputField" onAction="#enterPressed" GridPane.rowIndex="2"/>
|
||||
<MediaView fx:id="mediaView" fitHeight="200.0" fitWidth="200.0" GridPane.columnSpan="2147483647"
|
||||
GridPane.halignment="CENTER"/>
|
||||
<PrefixSelectionComboBox fx:id="typeCombobox" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="1"/>
|
||||
<PrefixSelectionComboBox fx:id="typeCombobox" managed="false" visible="false" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="2"/>
|
||||
<Label fx:id="progressLabel" text="Label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER"/>
|
||||
<VBox fx:id="guessesBox" GridPane.halignment="CENTER" GridPane.rowIndex="3"/>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
|
||||
@@ -2,27 +2,28 @@
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.media.MediaView?>
|
||||
<GridPane fx:id="grid" hgap="10.0" vgap="10.0" xmlns:fx="http://javafx.com/fxml/1"
|
||||
xmlns="http://javafx.com/javafx/24.0.1"
|
||||
fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticeSoundMultichoicePictureController">
|
||||
|
||||
<GridPane fx:id="grid" hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/24.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticeSoundMultichoicePictureController">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<MediaView fx:id="mediaView" fitHeight="200.0" fitWidth="200.0" GridPane.columnSpan="2147483647"
|
||||
GridPane.halignment="CENTER"/>
|
||||
<Button fx:id="validateButton" mnemonicParsing="false" onAction="#validatePressed"
|
||||
text="%practice.sound.multichoice.picture.validate.button.label" GridPane.columnSpan="2147483647"
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="1"/>
|
||||
<MediaView fx:id="mediaView" fitHeight="200.0" fitWidth="200.0" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1" />
|
||||
<Button fx:id="confirmButton" mnemonicParsing="false" onAction="#confirmPressed" text="%practice.sound.multichoice.picture.validate.button.label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
|
||||
<Label fx:id="progressLabel" text="Label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" />
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
</GridPane>
|
||||
|
||||
@@ -2,27 +2,28 @@
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.media.MediaView?>
|
||||
<GridPane fx:id="grid" hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/24.0.1"
|
||||
xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticeSoundMultichoiceController">
|
||||
|
||||
<GridPane fx:id="grid" hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/24.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticeSoundMultichoiceController">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
<RowConstraints vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<MediaView fx:id="mediaView" fitHeight="200.0" fitWidth="200.0" GridPane.columnSpan="2147483647"
|
||||
GridPane.halignment="CENTER"/>
|
||||
<Button fx:id="validateButton" mnemonicParsing="false" onAction="#validatePressed"
|
||||
text="%practice.sound.multichoice.validate.button.label" GridPane.columnSpan="2147483647"
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="1"/>
|
||||
<MediaView fx:id="mediaView" fitHeight="200.0" fitWidth="200.0" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1" />
|
||||
<Button fx:id="confirmButton" mnemonicParsing="false" onAction="#confirmPressed" text="%practice.sound.multichoice.validate.button.label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
|
||||
<Label fx:id="progressLabel" text="Label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" />
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
</GridPane>
|
||||
|
||||
@@ -16,5 +16,17 @@
|
||||
<groupId>ch.gtache.fro.gui</groupId>
|
||||
<artifactId>gui-fx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.gtache.fro</groupId>
|
||||
<artifactId>fro-vogelwarte</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.gtache.fro</groupId>
|
||||
<artifactId>fro-oiseaux-net</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -18,6 +18,9 @@ public final class FroApplication extends Application {
|
||||
final var loader = component.getMainLoader();
|
||||
loader.load();
|
||||
stage.setScene(new Scene(loader.getRoot()));
|
||||
stage.sizeToScene();
|
||||
stage.setWidth(820);
|
||||
stage.setHeight(900);
|
||||
stage.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package ch.gtache.fro.gui.run;
|
||||
|
||||
import javafx.application.Application;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* The main class
|
||||
*/
|
||||
public final class Main {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(Main.class);
|
||||
|
||||
private Main() {
|
||||
}
|
||||
|
||||
@@ -16,6 +20,7 @@ public final class Main {
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(final String[] args) {
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> logger.error("Uncaught exception on thread {}", t, e));
|
||||
Application.launch(FroApplication.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ package ch.gtache.fro.gui.run.modules;
|
||||
import ch.gtache.fro.modules.gui.fx.FXModule;
|
||||
import ch.gtache.fro.modules.gui.impl.GuiCoreModule;
|
||||
import ch.gtache.fro.modules.impl.CoreModule;
|
||||
import ch.gtache.fro.modules.oiseaux.net.OiseauxNetModule;
|
||||
import ch.gtache.fro.modules.vogelwarte.VogelwarteModule;
|
||||
import dagger.Component;
|
||||
import jakarta.inject.Singleton;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {CoreModule.class, GuiCoreModule.class, FXModule.class})
|
||||
@Component(modules = {CoreModule.class, GuiCoreModule.class, FXModule.class, VogelwarteModule.class, OiseauxNetModule.class})
|
||||
public interface FroComponent {
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
module ch.gtache.fro.gui.run {
|
||||
requires ch.gtache.fro.gui.fx;
|
||||
requires ch.gtache.fro.core;
|
||||
requires dagger;
|
||||
requires java.compiler;
|
||||
requires javafx.graphics;
|
||||
requires javafx.fxml;
|
||||
requires jakarta.inject;
|
||||
requires org.apache.logging.log4j.core;
|
||||
requires ch.gtache.fro.gui.core;
|
||||
requires ch.gtache.fro.vogelwarte;
|
||||
requires ch.gtache.fro.oiseaux.net;
|
||||
|
||||
exports ch.gtache.fro.gui.run to javafx.graphics;
|
||||
}
|
||||
15
gui/run/src/main/resources/log4j2.xml
Normal file
15
gui/run/src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="warn">
|
||||
<Appenders>
|
||||
<!-- Console appender configuration -->
|
||||
<Console name="console" target="SYSTEM_OUT">
|
||||
<PatternLayout
|
||||
pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="console"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
Reference in New Issue
Block a user