Fixes questions marked as incorrect, improves result view

This commit is contained in:
2025-10-02 18:15:04 +02:00
parent d2da811868
commit 5914e19ae1
5 changed files with 70 additions and 38 deletions

View File

@@ -41,7 +41,7 @@ public class PracticeRunnerImpl implements PracticeRunner {
if (run.currentQuestionIndex() < run.parameters().questionsNumber()) { if (run.currentQuestionIndex() < run.parameters().questionsNumber()) {
final var question = questionGenerator.generate(run.parameters()); final var question = questionGenerator.generate(run.parameters());
final var currentQuestion = run.currentQuestion(); final var currentQuestion = run.currentQuestion();
if (currentQuestion.bird() == answer) { if (currentQuestion.bird().equals(answer)) {
final var newCorrectQuestions = new ArrayList<>(run.correctQuestions()); final var newCorrectQuestions = new ArrayList<>(run.correctQuestions());
newCorrectQuestions.add(currentQuestion); newCorrectQuestions.add(currentQuestion);
return new PracticeRunImpl(run.parameters(), run.currentQuestionIndex() + 1, question, newCorrectQuestions, run.failedQuestions()); return new PracticeRunImpl(run.parameters(), run.currentQuestionIndex() + 1, question, newCorrectQuestions, run.failedQuestions());

View File

@@ -1,8 +1,11 @@
package ch.gtache.fro.practice.gui; package ch.gtache.fro.practice.gui;
import ch.gtache.fro.gui.Model; import ch.gtache.fro.gui.Model;
import ch.gtache.fro.practice.PracticeQuestion;
import ch.gtache.fro.practice.PracticeResult; import ch.gtache.fro.practice.PracticeResult;
import java.util.List;
/** /**
* Model for the practice result view * Model for the practice result view
*/ */
@@ -37,16 +40,16 @@ public interface PracticeResultModel extends Model {
String failureLabel(); String failureLabel();
/** /**
* The text for the label of successful guesses * The list of successful questions
* *
* @return The text * @return The list of questions
*/ */
String successListLabel(); List<PracticeQuestion> successList();
/** /**
* The text for the label of failed guesses * The list of failed questions
* *
* @return The text * @return The list of questions
*/ */
String failureListLabel(); List<PracticeQuestion> failureList();
} }

View File

@@ -1,13 +1,18 @@
package ch.gtache.fro.practice.gui.fx; package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.BirdTranslator;
import ch.gtache.fro.practice.PracticeQuestion;
import ch.gtache.fro.practice.gui.PracticeResultController; import ch.gtache.fro.practice.gui.PracticeResultController;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Singleton; import jakarta.inject.Singleton;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
import java.util.Objects; import static java.util.Objects.requireNonNull;
/** /**
* FX implementation of {@link PracticeResultController} * FX implementation of {@link PracticeResultController}
@@ -16,28 +21,33 @@ import java.util.Objects;
public final class FXPracticeResultController implements PracticeResultController { public final class FXPracticeResultController implements PracticeResultController {
private final FXPracticeResultModel model; private final FXPracticeResultModel model;
private final BirdTranslator translator;
@FXML @FXML
private Label successNumberLabel; private Label successNumberLabel;
@FXML @FXML
private Label failureNumberLabel; private Label failureNumberLabel;
@FXML @FXML
private Label successListLabel; private ListView<PracticeQuestion> successList;
@FXML @FXML
private Label failureListLabel; private ListView<PracticeQuestion> failureList;
@FXML @FXML
private Button confirmButton; private Button confirmButton;
@Inject @Inject
FXPracticeResultController(final FXPracticeResultModel model) { FXPracticeResultController(final FXPracticeResultModel model,
this.model = Objects.requireNonNull(model); final BirdTranslator translator) {
this.model = requireNonNull(model);
this.translator = requireNonNull(translator);
} }
@FXML @FXML
private void initialize() { private void initialize() {
successNumberLabel.textProperty().bind(model.successLabelProperty()); successNumberLabel.textProperty().bind(model.successLabelProperty());
failureNumberLabel.textProperty().bind(model.failureLabelProperty()); failureNumberLabel.textProperty().bind(model.failureLabelProperty());
successListLabel.textProperty().bind(model.successListLabelProperty()); successList.setItems(model.successList());
failureListLabel.textProperty().bind(model.failureListLabelProperty()); failureList.setItems(model.failureList());
successList.setCellFactory(new QuestionListCellFactory());
failureList.setCellFactory(new QuestionListCellFactory());
} }
@FXML @FXML
@@ -54,4 +64,23 @@ public final class FXPracticeResultController implements PracticeResultControlle
public FXPracticeResultModel model() { public FXPracticeResultModel model() {
return model; return model;
} }
private class QuestionListCellFactory implements Callback<ListView<PracticeQuestion>, ListCell<PracticeQuestion>> {
@Override
public ListCell<PracticeQuestion> call(final ListView<PracticeQuestion> param) {
return new PracticeQuestionListCell();
}
private class PracticeQuestionListCell extends ListCell<PracticeQuestion> {
@Override
protected void updateItem(final PracticeQuestion item, final boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
setText(translator.translateAsync(item.bird()).join());
}
}
}
}
} }

View File

@@ -1,6 +1,6 @@
package ch.gtache.fro.practice.gui.fx; package ch.gtache.fro.practice.gui.fx;
import ch.gtache.fro.BirdTranslator; import ch.gtache.fro.practice.PracticeQuestion;
import ch.gtache.fro.practice.PracticeResult; import ch.gtache.fro.practice.PracticeResult;
import ch.gtache.fro.practice.gui.PracticeResultModel; import ch.gtache.fro.practice.gui.PracticeResultModel;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -9,8 +9,8 @@ import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper; import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import java.util.stream.Collectors; import javafx.collections.ObservableList;
/** /**
* FX implementation of {@link PracticeResultModel} * FX implementation of {@link PracticeResultModel}
@@ -21,20 +21,27 @@ public final class FXPracticeResultModel implements PracticeResultModel {
private final ObjectProperty<PracticeResult> practiceResult; private final ObjectProperty<PracticeResult> practiceResult;
private final ReadOnlyStringWrapper successLabel; private final ReadOnlyStringWrapper successLabel;
private final ReadOnlyStringWrapper failureLabel; private final ReadOnlyStringWrapper failureLabel;
private final ReadOnlyStringWrapper successListLabel; private final ObservableList<PracticeQuestion> successList;
private final ReadOnlyStringWrapper failureListLabel; private final ObservableList<PracticeQuestion> failureList;
@Inject @Inject
FXPracticeResultModel(final BirdTranslator translator) { FXPracticeResultModel() {
this.practiceResult = new SimpleObjectProperty<>(); this.practiceResult = new SimpleObjectProperty<>();
this.successLabel = new ReadOnlyStringWrapper(); this.successLabel = new ReadOnlyStringWrapper();
this.failureLabel = new ReadOnlyStringWrapper(); this.failureLabel = new ReadOnlyStringWrapper();
this.successListLabel = new ReadOnlyStringWrapper(); this.successList = FXCollections.observableArrayList();
this.failureListLabel = new ReadOnlyStringWrapper(); this.failureList = FXCollections.observableArrayList();
successLabel.bind(resultProperty().map(r -> r.correctQuestionsCount() + "/" + r.totalQuestions())); successLabel.bind(resultProperty().map(r -> r.correctQuestionsCount() + "/" + r.totalQuestions()));
failureLabel.bind(resultProperty().map(r -> r.failedQuestionsCount() + "/" + 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(", ")))); practiceResult.addListener((_, _, newValue) -> {
failureListLabel.bind(resultProperty().map(r -> r.failedQuestions().stream().map(pq -> translator.translateAsync(pq.bird()).join()).collect(Collectors.joining(", ")))); if (newValue == null) {
successList.clear();
failureList.clear();
} else {
successList.setAll(newValue.correctQuestions());
failureList.setAll(newValue.failedQuestions());
}
});
} }
@Override @Override
@@ -70,20 +77,12 @@ public final class FXPracticeResultModel implements PracticeResultModel {
} }
@Override @Override
public String successListLabel() { public ObservableList<PracticeQuestion> successList() {
return successListLabel.get(); return successList;
}
ReadOnlyStringProperty successListLabelProperty() {
return successListLabel.getReadOnlyProperty();
} }
@Override @Override
public String failureListLabel() { public ObservableList<PracticeQuestion> failureList() {
return failureListLabel.get(); return failureList;
}
ReadOnlyStringProperty failureListLabelProperty() {
return failureListLabel.getReadOnlyProperty();
} }
} }

View File

@@ -3,6 +3,7 @@
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.*?> <?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" <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"> fx:controller="ch.gtache.fro.practice.gui.fx.FXPracticeResultController">
@@ -23,8 +24,8 @@
<Label text="%practice.result.failure.label" GridPane.rowIndex="2"/> <Label text="%practice.result.failure.label" GridPane.rowIndex="2"/>
<Label fx:id="successNumberLabel" GridPane.columnIndex="1" GridPane.rowIndex="1"/> <Label fx:id="successNumberLabel" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Label fx:id="failureNumberLabel" GridPane.columnIndex="1" GridPane.rowIndex="2"/> <Label fx:id="failureNumberLabel" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
<Label fx:id="successListLabel" GridPane.columnIndex="2" GridPane.rowIndex="1"/> <ListView fx:id="successList" GridPane.columnIndex="2" GridPane.rowIndex="1"/>
<Label fx:id="failureListLabel" GridPane.columnIndex="2" GridPane.rowIndex="2"/> <ListView fx:id="failureList" GridPane.columnIndex="2" GridPane.rowIndex="2"/>
<Button fx:id="confirmButton" mnemonicParsing="false" onAction="#confirmPressed" <Button fx:id="confirmButton" mnemonicParsing="false" onAction="#confirmPressed"
text="%practice.result.confirm.button.label" GridPane.columnSpan="2147483647" text="%practice.result.confirm.button.label" GridPane.columnSpan="2147483647"
GridPane.halignment="CENTER" GridPane.rowIndex="3"/> GridPane.halignment="CENTER" GridPane.rowIndex="3"/>