Initial commit

This commit is contained in:
2025-08-28 22:38:53 +02:00
commit f15208fe6d
232 changed files with 16821 additions and 0 deletions

50
gui/fx/pom.xml Normal file
View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ch.gtache.fro.gui</groupId>
<artifactId>gui</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>gui-fx</artifactId>
<properties>
<javafx.version>24.0.2</javafx.version>
<controlsfx.version>11.2.2</controlsfx.version>
</properties>
<dependencies>
<dependency>
<groupId>ch.gtache.fro.gui</groupId>
<artifactId>gui-core</artifactId>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>${controlsfx.version}</version>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,61 @@
package ch.gtache.fro.gui.fx;
import ch.gtache.fro.Bird;
import ch.gtache.fro.Fetcher;
import ch.gtache.fro.gui.FetchController;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import org.controlsfx.control.CheckComboBox;
import org.controlsfx.control.PrefixSelectionComboBox;
import java.util.Objects;
/**
* FX implementation of {@link FetchController}
*/
@Singleton
public final class FXFetchController implements FetchController {
private final FXFetchModel model;
@FXML
private PrefixSelectionComboBox<Bird> birdCombobox;
@FXML
private Button fetchButton;
@FXML
private Button fetchAllButton;
@FXML
private CheckComboBox<Fetcher> providersCombobox;
@Inject
FXFetchController(final FXFetchModel model) {
this.model = Objects.requireNonNull(model);
}
@Override
public void fetch() {
throw new UnsupportedOperationException();
}
@Override
public void fetchAll() {
throw new UnsupportedOperationException();
}
@Override
public FXFetchModel model() {
return model;
}
@FXML
private void fetchBirdPressed() {
fetch();
}
@FXML
private void fetchAllPressed() {
fetchAll();
}
}

View File

@@ -0,0 +1,17 @@
package ch.gtache.fro.gui.fx;
import ch.gtache.fro.gui.FetchModel;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
/**
* FX implementation of {@link FetchModel}
*/
@Singleton
public final class FXFetchModel implements FetchModel {
@Inject
FXFetchModel() {
}
}

View File

@@ -0,0 +1,35 @@
package ch.gtache.fro.gui.fx;
import ch.gtache.fro.gui.MainController;
import ch.gtache.fro.practice.gui.fx.FXPracticeController;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import javafx.fxml.FXML;
import java.util.Objects;
/**
* FX implementation of {@link MainController}
*/
@Singleton
public final class FXMainController implements MainController {
@FXML
private FXFetchController fetchController;
@FXML
private FXSettingsController settingsController;
@FXML
private FXPracticeController practiceController;
private final FXMainModel model;
@Inject
FXMainController(final FXMainModel model) {
this.model = Objects.requireNonNull(model);
}
@Override
public FXMainModel model() {
return model;
}
}

View File

@@ -0,0 +1,17 @@
package ch.gtache.fro.gui.fx;
import ch.gtache.fro.gui.MainModel;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
/**
* FX implementation of {@link MainModel}
*/
@Singleton
public final class FXMainModel implements MainModel {
@Inject
FXMainModel() {
}
}

View File

@@ -0,0 +1,32 @@
package ch.gtache.fro.gui.fx;
import ch.gtache.fro.Bird;
import ch.gtache.fro.gui.SettingsController;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
import java.util.Objects;
/**
* FX implementation of {@link SettingsController}
*/
@Singleton
public final class FXSettingsController implements SettingsController {
private final FXSettingsModel model;
@FXML
private TableView<Bird> table;
@Inject
FXSettingsController(final FXSettingsModel model) {
this.model = Objects.requireNonNull(model);
}
@Override
public FXSettingsModel model() {
return model;
}
}

View File

@@ -0,0 +1,16 @@
package ch.gtache.fro.gui.fx;
import ch.gtache.fro.gui.SettingsModel;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
/**
* FX implementation of {@link SettingsModel}
*/
@Singleton
public final class FXSettingsModel implements SettingsModel {
@Inject
FXSettingsModel() {
}
}

View File

@@ -0,0 +1,104 @@
package ch.gtache.fro.modules.gui.fx;
import ch.gtache.fro.gui.FetchController;
import ch.gtache.fro.gui.FetchModel;
import ch.gtache.fro.gui.MainController;
import ch.gtache.fro.gui.MainModel;
import ch.gtache.fro.gui.SettingsController;
import ch.gtache.fro.gui.SettingsModel;
import ch.gtache.fro.gui.fx.FXFetchController;
import ch.gtache.fro.gui.fx.FXFetchModel;
import ch.gtache.fro.gui.fx.FXMainController;
import ch.gtache.fro.gui.fx.FXMainModel;
import ch.gtache.fro.gui.fx.FXSettingsController;
import ch.gtache.fro.gui.fx.FXSettingsModel;
import ch.gtache.fro.gui.impl.CombinedResourceBundle;
import ch.gtache.fro.modules.practice.gui.fx.FXPracticeModule;
import ch.gtache.fro.practice.gui.fx.FXPracticeController;
import ch.gtache.fro.practice.gui.fx.FXPracticePictureExactController;
import ch.gtache.fro.practice.gui.fx.FXPracticePictureMultichoiceController;
import ch.gtache.fro.practice.gui.fx.FXPracticeResultController;
import ch.gtache.fro.practice.gui.fx.FXPracticeSettingsController;
import ch.gtache.fro.practice.gui.fx.FXPracticeSoundExactController;
import ch.gtache.fro.practice.gui.fx.FXPracticeSoundMultichoiceController;
import ch.gtache.fro.practice.gui.fx.FXPracticeSoundMultichoicePictureController;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import jakarta.inject.Singleton;
import javafx.fxml.FXMLLoader;
/**
* Dagger module for the FX GUI package
*/
@Module(includes = FXPracticeModule.class)
public abstract class FXModule {
private FXModule() {
//Empty constructor
}
@Provides
@Singleton
static FXMLLoader providesFXMLLoader(final FXFetchController fetchController,
final FXMainController mainController,
final FXSettingsController settingsController,
final FXPracticeController practiceController,
final FXPracticeResultController practiceResultController,
final FXPracticeSettingsController practiceSettingsController,
final FXPracticePictureExactController practicePictureExactController,
final FXPracticePictureMultichoiceController practicePictureMultichoiceController,
final FXPracticeSoundExactController practiceSoundExactController,
final FXPracticeSoundMultichoiceController practiceSoundMultichoiceController,
final FXPracticeSoundMultichoicePictureController practiceSoundMultichoicePictureController,
final CombinedResourceBundle combinedResourceBundle) {
final var loader = new FXMLLoader(FXModule.class.getResource("/ch/gtache/fro/gui/fx/mainView.fxml"));
loader.setResources(combinedResourceBundle);
loader.setControllerFactory(c -> {
if (c == FXFetchController.class) {
return fetchController;
} else if (c == FXMainController.class) {
return mainController;
} else if (c == FXSettingsController.class) {
return settingsController;
} else if (c == FXPracticeController.class) {
return practiceController;
} else if (c == FXPracticeResultController.class) {
return practiceResultController;
} else if (c == FXPracticeSettingsController.class) {
return practiceSettingsController;
} else if (c == FXPracticePictureExactController.class) {
return practicePictureExactController;
} else if (c == FXPracticePictureMultichoiceController.class) {
return practicePictureMultichoiceController;
} else if (c == FXPracticeSoundExactController.class) {
return practiceSoundExactController;
} else if (c == FXPracticeSoundMultichoiceController.class) {
return practiceSoundMultichoiceController;
} else if (c == FXPracticeSoundMultichoicePictureController.class) {
return practiceSoundMultichoicePictureController;
} else {
throw new IllegalArgumentException("Unknown controller class " + c);
}
});
return loader;
}
@Binds
abstract FetchController bindsFetchController(final FXFetchController fetchController);
@Binds
abstract FetchModel bindsFetchModel(final FXFetchModel fetchModel);
@Binds
abstract MainController bindsMainController(final FXMainController mainController);
@Binds
abstract MainModel bindsMainModel(final FXMainModel mainModel);
@Binds
abstract SettingsController bindsSettingsController(final FXSettingsController settingsController);
@Binds
abstract SettingsModel bindsSettingsModel(final FXSettingsModel settingsModel);
}

View File

@@ -0,0 +1,59 @@
package ch.gtache.fro.modules.practice.gui.fx;
import ch.gtache.fro.practice.gui.*;
import ch.gtache.fro.practice.gui.fx.*;
import dagger.Binds;
import dagger.Module;
/**
* Dagger module for the FX practice package
*/
@Module
public abstract class FXPracticeModule {
private FXPracticeModule() {
//Empty constructor
}
@Binds
abstract PracticeController bindsPracticeController(final FXPracticeController practiceController);
@Binds
abstract PracticeModel bindsPracticeModel(final FXPracticeModel practiceModel);
@Binds
abstract PracticeSettingsController bindsPracticeSettingsController(final FXPracticeSettingsController practiceSettingsController);
@Binds
abstract PracticeSettingsModel bindsPracticeSettingsModel(final FXPracticeSettingsModel practiceSettingsModel);
@Binds
abstract PracticePictureExactController bindsPracticePictureExactController(final FXPracticePictureExactController practicePictureExactController);
@Binds
abstract PracticePictureExactModel bindsPracticePictureExactModel(final FXPracticePictureExactModel practicePictureExactModel);
@Binds
abstract PracticePictureMultichoiceController bindsPracticePictureMultichoiceController(final FXPracticePictureMultichoiceController practicePictureMultichoiceController);
@Binds
abstract PracticePictureMultichoiceModel bindsPracticePictureMultichoiceModel(final FXPracticePictureMultichoiceModel practicePictureMultichoiceModel);
@Binds
abstract PracticeSoundExactController bindsPracticeSoundExactController(final FXPracticeSoundExactController practiceSoundExactController);
@Binds
abstract PracticeSoundExactModel bindsPracticeSoundExactModel(final FXPracticeSoundExactModel practiceSoundExactModel);
@Binds
abstract PracticeSoundMultichoiceController bindsPracticeSoundMultichoiceController(final FXPracticeSoundMultichoiceController practiceSoundMultichoiceController);
@Binds
abstract PracticeSoundMultichoiceModel bindsPracticeSoundMultichoiceModel(final FXPracticeSoundMultichoiceModel practiceSoundMultichoiceModel);
@Binds
abstract PracticeSoundMultichoicePictureController bindsPracticeSoundMultichoicePictureController(final FXPracticeSoundMultichoicePictureController practiceSoundMultichoicePictureController);
@Binds
abstract PracticeSoundMultichoicePictureModel bindsPracticeSoundMultichoicePictureModel(final FXPracticeSoundMultichoicePictureModel practiceSoundMultichoicePictureModel);
}

View File

@@ -0,0 +1,56 @@
package ch.gtache.fro.practice.gui.fx;
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;
/**
* FX implementation of {@link PracticeController}
*/
@Singleton
public final class FXPracticeController implements PracticeController {
@FXML
private Pane practiceSettings;
@FXML
private Pane practiceResult;
@FXML
private Pane practicePictureExact;
@FXML
private Pane practicePictureMultichoice;
@FXML
private Pane practiceSoundExact;
@FXML
private Pane practiceSoundMultichoice;
@FXML
private Pane practiceSoundMultichoicePicture;
private final FXPracticeModel model;
@Inject
FXPracticeController(final FXPracticeModel model) {
this.model = Objects.requireNonNull(model);
}
@FXML
private void initialize() {
model.panes().addAll(practiceSettings, practiceResult, practicePictureExact, practicePictureMultichoice,
practiceSoundExact, practiceSoundMultichoice, practiceSoundMultichoicePicture);
model.selectedPaneProperty().addListener((_, _, newValue) -> {
for (final var pane : model.panes()) {
pane.setManaged(pane == newValue);
pane.setVisible(pane == newValue);
}
});
model.setSelectedPane(practiceSettings);
}
@Override
public FXPracticeModel model() {
return model;
}
}

View File

@@ -0,0 +1,42 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.practice.gui.PracticeModel;
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.scene.layout.Pane;
/**
* FX implementation of {@link PracticeModel}
*/
@Singleton
public final class FXPracticeModel implements PracticeModel {
private final ObservableList<Pane> panes;
private final ObjectProperty<Pane> selectedPane;
@Inject
FXPracticeModel() {
this.panes = FXCollections.observableArrayList();
this.selectedPane = new SimpleObjectProperty<>();
}
ObservableList<Pane> panes() {
return panes;
}
Pane selectedPane() {
return selectedPane.get();
}
void setSelectedPane(final Pane pane) {
selectedPane.set(pane);
}
ObjectProperty<Pane> selectedPaneProperty() {
return selectedPane;
}
}

View File

@@ -0,0 +1,56 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.PictureType;
import ch.gtache.fro.practice.gui.PracticePictureExactController;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import org.controlsfx.control.PrefixSelectionComboBox;
import org.controlsfx.control.textfield.CustomTextField;
import java.util.Objects;
/**
* FX implementation of {@link PracticePictureExactController}
*/
@Singleton
public final class FXPracticePictureExactController implements PracticePictureExactController {
private final FXPracticePictureExactModel model;
@FXML
private ImageView pictureView;
@FXML
private CustomTextField nameField;
@FXML
private PrefixSelectionComboBox<PictureType> typeCombobox;
@FXML
private Button validateButton;
@Inject
FXPracticePictureExactController(final FXPracticePictureExactModel model) {
this.model = Objects.requireNonNull(model);
}
@Override
public void confirm() {
throw new UnsupportedOperationException();
}
@Override
public FXPracticePictureExactModel model() {
return model;
}
@FXML
private void enterPressed() {
confirm();
}
@FXML
private void validatePressed() {
confirm();
}
}

View File

@@ -0,0 +1,65 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.Picture;
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;
/**
* FX implementation of {@link PracticePictureExactModel}
*/
@Singleton
public final class FXPracticePictureExactModel implements PracticePictureExactModel {
private final StringProperty guess;
private final ObjectProperty<Picture> picture;
@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;
}
@Override
public Picture picture() {
return picture.get();
}
@Override
public void picture(final Picture picture) {
this.picture.set(picture);
}
/**
* Returns the property for the picture
*
* @return the property for the picture
*/
ObjectProperty<Picture> pictureProperty() {
return picture;
}
}

View File

@@ -0,0 +1,45 @@
package ch.gtache.fro.practice.gui.fx;
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.layout.GridPane;
import java.util.Objects;
/**
* FX implementation of {@link PracticePictureMultichoiceController}
*/
@Singleton
public final class FXPracticePictureMultichoiceController implements PracticePictureMultichoiceController {
private final FXPracticePictureMultichoiceModel model;
@FXML
private GridPane grid;
@FXML
private Button validateButton;
@Inject
FXPracticePictureMultichoiceController(final FXPracticePictureMultichoiceModel model) {
this.model = Objects.requireNonNull(model);
}
@Override
public FXPracticePictureMultichoiceModel model() {
return model;
}
@Override
public void confirm() {
throw new UnsupportedOperationException();
}
@FXML
private void validatePressed() {
confirm();
}
}

View File

@@ -0,0 +1,73 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.Bird;
import ch.gtache.fro.Picture;
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;
/**
* FX implementation of {@link PracticePictureMultichoiceModel}
*/
@Singleton
public final class FXPracticePictureMultichoiceModel implements PracticePictureMultichoiceModel {
private final ObjectProperty<Picture> picture;
private final ObservableList<Bird> suggestions;
private final ObjectProperty<Bird> selected;
@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;
}
@Override
public Picture picture() {
return picture.get();
}
@Override
public void picture(final Picture picture) {
this.picture.set(picture);
}
/**
* Returns the property for the picture
*
* @return the property for the picture
*/
ObjectProperty<Picture> pictureProperty() {
return picture;
}
}

View File

@@ -0,0 +1,36 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.practice.gui.PracticeResultController;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import java.util.Objects;
/**
* FX implementation of {@link PracticeResultController}
*/
@Singleton
public final class FXPracticeResultController implements PracticeResultController {
private final FXPracticeResultModel model;
@FXML
private Label successNumberLabel;
@FXML
private Label failureNumberLabel;
@FXML
private Label successListLabel;
@FXML
private Label failureListLabel;
@Inject
FXPracticeResultController(final FXPracticeResultModel model) {
this.model = Objects.requireNonNull(model);
}
@Override
public FXPracticeResultModel model() {
return model;
}
}

View File

@@ -0,0 +1,18 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.practice.gui.PracticeResultModel;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
/**
* FX implementation of {@link PracticeResultModel}
*/
@Singleton
public final class FXPracticeResultModel implements PracticeResultModel {
@Inject
FXPracticeResultModel() {
}
}

View File

@@ -0,0 +1,61 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.practice.PracticeType;
import ch.gtache.fro.practice.gui.PracticeSettingsController;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import org.controlsfx.control.PrefixSelectionComboBox;
import static java.util.Objects.requireNonNull;
/**
* FX implementation of {@link PracticeSettingsController}
*/
@Singleton
public final class FXPracticeSettingsController implements PracticeSettingsController {
private final FXPracticeSettingsModel model;
private final PracticeTypeConverter practiceTypeConverter;
@FXML
private PrefixSelectionComboBox<PracticeType> practiceTypeCombobox;
@FXML
private Label suggestionsNumberLabel;
@FXML
private Spinner<Integer> suggestionsNumberSpinner;
@Inject
FXPracticeSettingsController(final FXPracticeSettingsModel model, final PracticeTypeConverter practiceTypeConverter) {
this.model = requireNonNull(model);
this.practiceTypeConverter = requireNonNull(practiceTypeConverter);
}
@FXML
private void initialize() {
practiceTypeCombobox.setItems(model.practiceTypes());
practiceTypeCombobox.valueProperty().bindBidirectional(model.practiceTypeProperty());
practiceTypeCombobox.setConverter(practiceTypeConverter);
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());
}
@Override
public void startPractice() {
throw new UnsupportedOperationException();
}
@FXML
private void startPressed() {
startPractice();
}
@Override
public FXPracticeSettingsModel model() {
return model;
}
}

View File

@@ -0,0 +1,94 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.practice.PracticeType;
import ch.gtache.fro.practice.gui.PracticeSettingsModel;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
/**
* FX implementation of {@link PracticeSettingsModel}
*/
@Singleton
public final class FXPracticeSettingsModel implements PracticeSettingsModel {
private static final int DEFAULT_SUGGESTIONS_NUMBER = 4;
private final ObservableList<PracticeType> practiceTypes;
private final ObjectProperty<PracticeType> practiceType;
private final ReadOnlyBooleanWrapper hasSuggestions;
private final IntegerProperty suggestionsNumber;
@Inject
FXPracticeSettingsModel() {
this.practiceTypes = FXCollections.observableArrayList(PracticeType.values());
this.practiceType = new SimpleObjectProperty<>(PracticeType.PICTURE_EXACT);
this.hasSuggestions = new ReadOnlyBooleanWrapper(true);
hasSuggestions.bind(practiceType.isEqualTo(PracticeType.PICTURE_EXACT).or(practiceType.isEqualTo(PracticeType.SOUND_EXACT)));
this.suggestionsNumber = new SimpleIntegerProperty(DEFAULT_SUGGESTIONS_NUMBER);
}
@Override
public ObservableList<PracticeType> practiceTypes() {
return practiceTypes;
}
@Override
public PracticeType practiceType() {
return practiceType.get();
}
@Override
public void practiceType(final PracticeType practiceType) {
this.practiceType.set(practiceType);
}
/**
* Returns the practice type property
*
* @return The practice type property
*/
ObjectProperty<PracticeType> practiceTypeProperty() {
return practiceType;
}
@Override
public boolean hasSuggestions() {
return hasSuggestions.get();
}
/**
* Returns the has suggestions property
*
* @return The has suggestions property
*/
ReadOnlyBooleanProperty hasSuggestionsProperty() {
return hasSuggestions.getReadOnlyProperty();
}
@Override
public int suggestionsNumber() {
return suggestionsNumber.get();
}
@Override
public void suggestionsNumber(final int suggestionsNumber) {
this.suggestionsNumber.set(suggestionsNumber);
}
/**
* Returns the suggestions number property
*
* @return The suggestions number property
*/
IntegerProperty suggestionsNumberProperty() {
return suggestionsNumber;
}
}

View File

@@ -0,0 +1,57 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.SoundType;
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.TextField;
import javafx.scene.media.MediaView;
import org.controlsfx.control.PrefixSelectionComboBox;
import java.util.Objects;
/**
* FX implementation of {@link PracticeSoundExactController}
*/
@Singleton
public final class FXPracticeSoundExactController implements PracticeSoundExactController {
private final FXPracticeSoundExactModel model;
@FXML
private Button validateButton;
@FXML
private TextField inputField;
@FXML
private MediaView mediaView;
@FXML
private PrefixSelectionComboBox<SoundType> typeCombobox;
@Inject
FXPracticeSoundExactController(final FXPracticeSoundExactModel model) {
this.model = Objects.requireNonNull(model);
}
@Override
public FXPracticeSoundExactModel model() {
return model;
}
@Override
public void confirm() {
throw new UnsupportedOperationException();
}
@FXML
private void validatePressed() {
confirm();
}
@FXML
private void enterPressed() {
confirm();
}
}

View File

@@ -0,0 +1,65 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.Sound;
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;
/**
* FX implementation of {@link PracticeSoundExactModel}
*/
@Singleton
public final class FXPracticeSoundExactModel implements PracticeSoundExactModel {
private final StringProperty guess;
private final ObjectProperty<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;
}
@Override
public Sound sound() {
return sound.get();
}
@Override
public void sound(final Sound sound) {
this.sound.set(sound);
}
/**
* Returns the property for the sound
*
* @return the property for the sound
*/
ObjectProperty<Sound> soundProperty() {
return sound;
}
}

View File

@@ -0,0 +1,48 @@
package ch.gtache.fro.practice.gui.fx;
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.layout.GridPane;
import javafx.scene.media.MediaView;
import java.util.Objects;
/**
* FX implementation of {@link PracticeSoundMultichoiceController}
*/
@Singleton
public final class FXPracticeSoundMultichoiceController implements PracticeSoundMultichoiceController {
private final FXPracticeSoundMultichoiceModel model;
@FXML
private GridPane grid;
@FXML
private MediaView mediaView;
@FXML
private Button validateButton;
@Inject
FXPracticeSoundMultichoiceController(final FXPracticeSoundMultichoiceModel model) {
this.model = Objects.requireNonNull(model);
}
@Override
public FXPracticeSoundMultichoiceModel model() {
return model;
}
@Override
public void confirm() {
throw new UnsupportedOperationException();
}
@FXML
private void validatePressed() {
confirm();
}
}

View File

@@ -0,0 +1,73 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.Bird;
import ch.gtache.fro.Sound;
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;
/**
* FX implementation of {@link PracticeSoundMultichoiceModel}
*/
@Singleton
public final class FXPracticeSoundMultichoiceModel implements PracticeSoundMultichoiceModel {
private final ObservableList<Bird> suggestions;
private final ObjectProperty<Bird> selected;
private final ObjectProperty<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;
}
@Override
public Sound sound() {
return sound.get();
}
@Override
public void sound(final Sound sound) {
this.sound.set(sound);
}
/**
* Returns the property for the sound
*
* @return the property for the sound
*/
ObjectProperty<Sound> soundProperty() {
return sound;
}
}

View File

@@ -0,0 +1,48 @@
package ch.gtache.fro.practice.gui.fx;
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.layout.GridPane;
import javafx.scene.media.MediaView;
import java.util.Objects;
/**
* FX implementation of {@link PracticeSoundMultichoicePictureController}
*/
@Singleton
public final class FXPracticeSoundMultichoicePictureController implements PracticeSoundMultichoicePictureController {
private final FXPracticeSoundMultichoicePictureModel model;
@FXML
private GridPane grid;
@FXML
private MediaView mediaView;
@FXML
private Button validateButton;
@Inject
FXPracticeSoundMultichoicePictureController(final FXPracticeSoundMultichoicePictureModel model) {
this.model = Objects.requireNonNull(model);
}
@Override
public FXPracticeSoundMultichoicePictureModel model() {
return model;
}
@Override
public void confirm() {
throw new UnsupportedOperationException();
}
@FXML
private void validatePressed() {
confirm();
}
}

View File

@@ -0,0 +1,73 @@
package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.Picture;
import ch.gtache.fro.Sound;
import ch.gtache.fro.practice.gui.PracticeSoundMultichoicePictureModel;
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 PracticeSoundMultichoicePictureModel}
*/
@Singleton
public final class FXPracticeSoundMultichoicePictureModel implements PracticeSoundMultichoicePictureModel {
private final ObservableList<Picture> suggestions;
private final ObjectProperty<Picture> selected;
private final ObjectProperty<Sound> sound;
@Inject
FXPracticeSoundMultichoicePictureModel() {
this.suggestions = FXCollections.observableArrayList();
this.selected = new SimpleObjectProperty<>();
this.sound = new SimpleObjectProperty<>();
}
@Override
public ObservableList<Picture> suggestions() {
return suggestions;
}
@Override
public Picture selected() {
return selected.get();
}
@Override
public void selected(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;
}
@Override
public Sound sound() {
return sound.get();
}
@Override
public void sound(final Sound sound) {
this.sound.set(sound);
}
/**
* Returns the property for the sound
*
* @return the property for the sound
*/
ObjectProperty<Sound> soundProperty() {
return sound;
}
}

View File

@@ -0,0 +1,33 @@
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;
}
}

View File

@@ -0,0 +1,22 @@
/**
* GUI FX module for the FRO project
*/
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 javafx.graphics;
requires javafx.media;
requires org.controlsfx.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;
opens ch.gtache.fro.gui.fx to javafx.fxml;
opens ch.gtache.fro.practice.gui.fx to javafx.fxml;
}

View File

@@ -0,0 +1,33 @@
<?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.*?>
<?import org.controlsfx.control.CheckComboBox?>
<?import org.controlsfx.control.PrefixSelectionComboBox?>
<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.gui.fx.FXFetchController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES"/>
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<PrefixSelectionComboBox fx:id="birdCombobox" GridPane.rowIndex="1"/>
<Button fx:id="fetchButton" mnemonicParsing="false" onAction="#fetchBirdPressed"
text="%fetch.button.fetch.label" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Button fx:id="fetchAllButton" mnemonicParsing="false" onAction="#fetchAllPressed"
text="%fetch.button.all.label" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
<Label text="%fetch.providers.label"/>
<CheckComboBox fx:id="providersCombobox" GridPane.columnIndex="1"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</GridPane>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<TabPane maxHeight="-Infinity" maxWidth="-Infinity" tabClosingPolicy="UNAVAILABLE" xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/24.0.1" fx:controller="ch.gtache.fro.gui.fx.FXMainController">
<tabs>
<Tab closable="false" text="%main.tab.practice.label">
<fx:include source="../../practice/gui/fx/practiceView.fxml" fx:id="practice"/>
</Tab>
<Tab closable="false" text="%main.tab.settings.label">
<fx:include source="settingsView.fxml" fx:id="settings"/>
</Tab>
<Tab closable="false" text="%main.tab.fetch.label">
<fx:include source="fetchView.fxml" fx:id="fetch"/>
</Tab>
</tabs>
</TabPane>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" xmlns="http://javafx.com/javafx/24.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.gtache.fro.gui.fx.FXSettingsController">
<children>
<TableView fx:id="table" layoutX="200.0" layoutY="68.0" AnchorPane.bottomAnchor="10.0"
AnchorPane.leftAnchor="10.0"
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
<columns>
<TableColumn maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="75.0"
text="%settings.table.column.enabled"/>
<TableColumn maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="75.0"
text="%settings.table.column.bird"/>
<TableColumn maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="75.0"
text="%settings.table.column.fetchers"/>
<TableColumn maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="75.0"
text="%settings.table.column.pictures"/>
<TableColumn maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="75.0"
text="%settings.table.column.sounds"/>
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
</TableView>
</children>
</AnchorPane>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?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">
<columnConstraints>
<ColumnConstraints hgrow="ALWAYS"/>
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="ALWAYS"/>
<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"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</GridPane>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?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"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticePictureMultichoiceController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES"/>
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<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"
text="%practice.picture.multichoice.validate.button.label" GridPane.columnSpan="2147483647"
GridPane.halignment="CENTER" GridPane.rowIndex="1"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</GridPane>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?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"
fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticeResultController">
<columnConstraints>
<ColumnConstraints hgrow="NEVER"/>
<ColumnConstraints hgrow="SOMETIMES"/>
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<Label text="%practice.result.label" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER"/>
<Label text="%practice.result.success.label" GridPane.rowIndex="1"/>
<Label text="%practice.result.failure.label" GridPane.rowIndex="2"/>
<Label fx:id="successNumberLabel" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<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"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</GridPane>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Spinner?>
<?import javafx.scene.layout.*?>
<?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">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES"/>
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<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"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</GridPane>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.media.MediaView?>
<?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.FXPracticeSoundExactController">
<columnConstraints>
<ColumnConstraints hgrow="ALWAYS"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<Button fx:id="validateButton" mnemonicParsing="false" onAction="#validatePressed"
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"/>
<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"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</GridPane>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.*?>
<?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">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES"/>
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<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"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</GridPane>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.*?>
<?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">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES"/>
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<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"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</GridPane>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<StackPane xmlns="http://javafx.com/javafx/24.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticeController">
<children>
<fx:include source="practiceResultView.fxml" fx:id="practiceResult"/>
<fx:include source="practiceSettingsView.fxml" fx:id="practiceSettings"/>
<fx:include source="practicePictureMultichoiceView.fxml" fx:id="practicePictureMultichoice"/>
<fx:include source="practicePictureExactView.fxml" fx:id="practicePictureExact"/>
<fx:include source="practiceSoundMultichoiceView.fxml" fx:id="practiceSoundMultichoice"/>
<fx:include source="practiceSoundMultichoicePictureView.fxml" fx:id="practiceSoundMultichoicePicture"/>
<fx:include source="practiceSoundExactView.fxml" fx:id="practiceSoundExact"/>
</children>
</StackPane>