feat: add custom data collection settings

Add support for storing and configuring custom student data
collections in AppState.

Introduce a custom data settings window for entering and validating
student fields, selecting an object index, previewing student data,
and applying prepared data to the current collection.

Refactor collection creation to use reusable input handling and
update collection display and message window usage.
This commit is contained in:
2026-08-28 19:51:47 +04:00
parent 5d0052805c
commit ff0c063078
4 changed files with 350 additions and 29 deletions
@@ -6,20 +6,31 @@ import ru.project.student.Student;
public class AppState {
private MyList<Student> collection;
private MyList<Student> currentCollection;
private MyList<Student> customDataCollection;
private String currentFilePath;
public AppState() {
this.collection = new MyLinkedList<>();
this.currentCollection = new MyLinkedList<>();
this.customDataCollection = new MyLinkedList<>();
this.currentFilePath = null;
}
public MyList<Student> getCollection() {
return collection;
public MyList<Student> getCurrentCollection() {
return currentCollection;
}
public void setCollection(MyList<Student> collection) {
this.collection = collection;
public void setCurrentCollection(MyList<Student> currentCollection) {
this.currentCollection = currentCollection;
}
public MyList<Student> getCustomDataCollection() {
return customDataCollection;
}
public void setCustomDataCollection(MyList<Student> customDataCollection) {
this.customDataCollection = customDataCollection;
}
public String getCurrentFilePath() {
@@ -8,7 +8,8 @@ import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
import ru.project.model.AppState;
import ru.project.model.FillMethod;
import ru.project.ui.base.BaseWindow;
import ru.project.ui.window.CollectionSizeInputWindow;
import ru.project.ui.window.CustomDataSettingsWindow;
import ru.project.ui.window.InputValueWindow;
import ru.project.ui.window.MessageWindow;
public class CreateCollectionMenu extends BaseWindow {
@@ -19,9 +20,13 @@ public class CreateCollectionMenu extends BaseWindow {
private final Label sizeLabel;
private final Label fillMethodLabel;
private final AppState state;
public CreateCollectionMenu(WindowBasedTextGUI gui, AppState state) {
super("Создание новой коллекции", gui);
this.state = state;
collectionSize = 0;
selectedFillMethod = FillMethod.CUSTOM;
@@ -35,9 +40,12 @@ public class CreateCollectionMenu extends BaseWindow {
panel.addComponent(sizeLabel);
panel.addComponent(fillMethodLabel);
panel.addComponent(new Button("Задать размер", this::openCollectionSizeInputWindow));
panel.addComponent(new Button("Задать размер", this::openCollectionSizeInputValueWindow));
panel.addComponent(new Button("Настроить способ заполнения", this::openFillMethodMenu));
panel.addComponent(new Button("Выбрать способ заполнения", this::selectFillMethod));
panel.addComponent(
new Button("Настроить текущий способ заполнения", this::setupCurrentFillMethod));
panel.addComponent(new Button("Создать новую коллекцию", this::createCollection));
@@ -46,20 +54,53 @@ public class CreateCollectionMenu extends BaseWindow {
setComponent(panel);
}
private void openCollectionSizeInputWindow() {
CollectionSizeInputWindow window =
new CollectionSizeInputWindow(
private void openCollectionSizeInputValueWindow() {
InputValueWindow window =
new InputValueWindow(
gui,
size -> {
collectionSize = size;
sizeLabel.setText("Размер: " + collectionSize);
});
"Размер коллекции",
"Введите размер коллекции:",
collectionSize > 0 ? String.valueOf(collectionSize) : null,
this::handleSizeEntered);
window.show();
}
private void openFillMethodMenu() {
FillMethodSelectionWindow menu =
private boolean handleSizeEntered(String value) {
try {
int enteredSize = Integer.parseInt(value);
if (enteredSize <= 0) {
MessageWindow.show(gui, "Размер должен быть больше 0.");
return false;
}
collectionSize = enteredSize;
sizeLabel.setText("Размер: " + collectionSize);
return true;
} catch (NumberFormatException e) {
MessageWindow.show(gui, "Введите целое число.");
return false;
}
}
private void setupCurrentFillMethod() {
switch (selectedFillMethod) {
case CUSTOM -> {
BaseWindow window = new CustomDataSettingsWindow(gui, state, collectionSize);
window.show();
}
case FILE ->
// TODO
MessageWindow.show(gui, "Не реализовано.");
case RANDOM ->
MessageWindow.show(gui, "Для случайных данных дополнительные настройки не требуются.");
}
}
private void selectFillMethod() {
BaseWindow menu =
new FillMethodSelectionWindow(
gui,
fillMethod -> {
@@ -73,7 +114,7 @@ public class CreateCollectionMenu extends BaseWindow {
private void createCollection() {
if (collectionSize <= 0) {
MessageWindow.create(gui, "Сначала задайте размер коллекции.");
MessageWindow.show(gui, "Сначала задайте размер коллекции.");
return;
}
@@ -94,17 +135,17 @@ public class CreateCollectionMenu extends BaseWindow {
private void createCollectionFromCustomData() {
// TODO
MessageWindow.create(gui, "Не реализовано.");
MessageWindow.show(gui, "Не реализовано.");
}
private void createCollectionFromRandomData() {
// TODO
MessageWindow.create(gui, "Не реализовано.");
MessageWindow.show(gui, "Не реализовано.");
}
private void createCollectionFromFileData() {
// TODO
MessageWindow.create(gui, "Не реализовано.");
MessageWindow.show(gui, "Не реализовано.");
}
private String getFillMethodName(FillMethod fillMethod) {
@@ -2,9 +2,11 @@ package ru.project.ui.menu;
import com.googlecode.lanterna.gui2.ActionListBox;
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
import ru.project.collection.MyList;
import ru.project.model.AppState;
import ru.project.student.Student;
import ru.project.ui.base.BaseWindow;
import ru.project.ui.window.CurrentCollectionWindow;
import ru.project.ui.window.CollectionDisplayWindow;
import ru.project.ui.window.MessageWindow;
public class MainMenu extends BaseWindow {
@@ -37,35 +39,37 @@ public class MainMenu extends BaseWindow {
}
private void showCurrentCollection() {
CurrentCollectionWindow window = new CurrentCollectionWindow(gui, state);
MyList<Student> currentCollection = state.getCurrentCollection();
BaseWindow window = new CollectionDisplayWindow(gui, currentCollection);
window.show();
}
private void createCollection() {
CreateCollectionMenu window = new CreateCollectionMenu(gui, state);
BaseWindow window = new CreateCollectionMenu(gui, state);
window.show();
}
private void saveCollection() {
// TODO
MessageWindow.create(gui, "Не реализовано.");
MessageWindow.show(gui, "Не реализовано.");
}
private void sortCollection() {
// TODO
MessageWindow.create(gui, "Не реализовано.");
MessageWindow.show(gui, "Не реализовано.");
}
private void searchCollection() {
// TODO
MessageWindow.create(gui, "Не реализовано.");
MessageWindow.show(gui, "Не реализовано.");
}
private void countOccurrences() {
// TODO
MessageWindow.create(gui, "Не реализовано.");
MessageWindow.show(gui, "Не реализовано.");
}
private void exit() {
@@ -0,0 +1,265 @@
package ru.project.ui.window;
import com.googlecode.lanterna.gui2.Button;
import com.googlecode.lanterna.gui2.Label;
import com.googlecode.lanterna.gui2.LinearLayout;
import com.googlecode.lanterna.gui2.Panel;
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
import ru.project.collection.MyLinkedList;
import ru.project.collection.MyList;
import ru.project.model.AppState;
import ru.project.student.Student;
import ru.project.student.StudentBuilder;
import ru.project.ui.base.BaseWindow;
public class CustomDataSettingsWindow extends BaseWindow {
private final AppState state;
private final int collectionSize;
private final StudentBuilder studentBuilder;
private String groupNumber;
private Double averageGrade;
private Integer recordBookNumber;
private Integer selectedObjectIndex;
private final Label groupNumberLabel;
private final Label averageGradeLabel;
private final Label recordBookNumberLabel;
private final Label indexLabel;
private final MyList<Student> preparedCustomDataCollection;
public CustomDataSettingsWindow(WindowBasedTextGUI gui, AppState state, int collectionSize) {
super("Настройка пользовательских данных", gui);
this.state = state;
this.collectionSize = collectionSize;
this.studentBuilder = new StudentBuilder();
this.preparedCustomDataCollection = new MyLinkedList<>();
MyList<Student> customDataCollection = state.getCustomDataCollection();
int elementsToCopy = Math.min(customDataCollection.size(), collectionSize);
for (int i = 0; i < elementsToCopy; i++) {
preparedCustomDataCollection.add(customDataCollection.get(i));
}
for (int i = elementsToCopy; i < collectionSize; i++) {
preparedCustomDataCollection.add(null);
}
Panel panel = new Panel();
panel.setLayoutManager(new LinearLayout());
groupNumberLabel = new Label("Номер группы: не задан");
averageGradeLabel = new Label("Средний балл: не задан");
recordBookNumberLabel = new Label("Номер зачётной книжки: не задан");
indexLabel = new Label("Индекс объекта: не задан");
panel.addComponent(groupNumberLabel);
panel.addComponent(averageGradeLabel);
panel.addComponent(recordBookNumberLabel);
panel.addComponent(indexLabel);
panel.addComponent(new Button("Задать номер группы", this::setGroupNumber));
panel.addComponent(new Button("Задать средний балл", this::setAverageGrade));
panel.addComponent(new Button("Задать номер зачётной книжки", this::setRecordBookNumber));
panel.addComponent(new Button("Задать индекс объекта", this::setObjectIndex));
panel.addComponent(new Button("Задать данные объекта", this::editObject));
panel.addComponent(new Button("Предпросмотр", this::preview));
panel.addComponent(new Button("Применить", this::apply));
panel.addComponent(new Button("Назад", this::close));
setComponent(panel);
}
private void setGroupNumber() {
InputValueWindow window =
new InputValueWindow(
gui,
"Номер группы",
"Введите номер группы (например, A12):",
groupNumber,
value -> {
if (!value.matches(Student.getGroupNumberPattern())) {
MessageWindow.show(gui, "Номер группы должен соответствовать формату A12.");
return false;
}
groupNumber = value;
studentBuilder.setGroupNumber(value);
groupNumberLabel.setText("Номер группы: " + groupNumber);
return true;
});
window.show();
}
private void setAverageGrade() {
InputValueWindow window =
new InputValueWindow(
gui,
"Средний балл",
"Введите средний балл от 0.0 до 5.0:",
averageGrade == null ? null : String.valueOf(averageGrade),
value -> {
try {
double parsedValue = Double.parseDouble(value);
if (parsedValue < Student.getMinAverageGrade()
|| parsedValue > Student.getMaxAverageGrade()) {
MessageWindow.show(gui, "Средний балл должен быть от 0.0 до 5.0.");
return false;
}
averageGrade = parsedValue;
studentBuilder.setAverageGrade(parsedValue);
averageGradeLabel.setText("Средний балл: " + averageGrade);
} catch (NumberFormatException e) {
MessageWindow.show(gui, "Введите корректное число.");
return false;
}
return true;
});
window.show();
}
private void setRecordBookNumber() {
InputValueWindow window =
new InputValueWindow(
gui,
"Номер зачётной книжки",
"Введите номер зачётной книжки:",
recordBookNumber == null ? null : String.valueOf(recordBookNumber),
value -> {
try {
int parsedValue = Integer.parseInt(value);
if (parsedValue < Student.getMinRecordBookNumber()) {
MessageWindow.show(gui, "Номер зачётной книжки должен быть положительным.");
return false;
}
recordBookNumber = parsedValue;
studentBuilder.setRecordBookNumber(parsedValue);
recordBookNumberLabel.setText("Номер зачётной книжки: " + recordBookNumber);
} catch (NumberFormatException e) {
MessageWindow.show(gui, "Введите целое число.");
return false;
}
return true;
});
window.show();
}
private void setObjectIndex() {
InputValueWindow window =
new InputValueWindow(
gui,
"Индекс объекта",
"Введите индекс объекта:",
selectedObjectIndex == null ? null : String.valueOf(selectedObjectIndex),
value -> {
try {
int index = Integer.parseInt(value);
if (index < 0) {
MessageWindow.show(gui, "Индекс объекта не может быть отрицательным.");
return false;
}
if (index >= collectionSize) {
MessageWindow.show(gui, "Индекс должен быть меньше размера коллекции.");
return false;
}
selectedObjectIndex = index;
indexLabel.setText("Индекс объекта: " + selectedObjectIndex);
return true;
} catch (NumberFormatException e) {
MessageWindow.show(gui, "Введите целое число.");
return false;
}
});
window.show();
}
private void editObject() {
if (!hasStudentData()) {
MessageWindow.show(gui, "Сначала задайте все поля студента.");
return;
}
if (selectedObjectIndex == null) {
MessageWindow.show(gui, "Сначала задайте индекс объекта.");
return;
}
if (selectedObjectIndex >= preparedCustomDataCollection.size()) {
MessageWindow.show(gui, "Индекс объекта выходит за границы подготовленной коллекции.");
return;
}
Student student = studentBuilder.build();
preparedCustomDataCollection.set(selectedObjectIndex, student);
MessageWindow.show(gui, "Данные объекта успешно заданы.");
}
private void apply() {
MyList<Student> currentCollection = new MyLinkedList<>();
for (int i = 0; i < preparedCustomDataCollection.size(); i++) {
currentCollection.add(preparedCustomDataCollection.get(i));
}
state.setCustomDataCollection(preparedCustomDataCollection);
state.setCurrentCollection(currentCollection);
close();
}
private void preview() {
if (!hasStudentData()) {
MessageWindow.show(gui, "Для предпросмотра сначала задайте все поля студента.");
return;
}
Student student = studentBuilder.build();
MessageWindow.show(gui, student.toString());
}
private boolean hasStudentData() {
return groupNumber != null && averageGrade != null && recordBookNumber != null;
}
}