// SPDX-FileCopyrightText: 2025 // SPDX-FileCopyrightText: 2025 // SPDX-FileCopyrightText: 2025 Jonas Smedegaard // SPDX-FileCopyrightText: 2025 // SPDX-License-Identifier: GPL-3.0-or-later package dk.biks.bachelorizer; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.TextArea; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.Stage; import java.io.IOException; import java.util.List; /// Bachelorizer - JavaFX Window view // Class is final to forbid subclassing, // because object is passed to controller during instantiation public final class Window extends Application { /// Default constructor // (declared explicitly only to silence javadoc) public Window() { } /// window width private static final int WINDOW_WIDTH = 1000; /// window height private static final int WINDOW_HEIGHT = 500; /// box width private static final int LIST_WIDTH = 250; /// box height private static final int LIST_HEIGHT = 35; /// Label styling private static final String LABEL_STYLE = "-fx-font-weight: bold;" + "-fx-font-size: 18;" + "-fx-padding: 10"; /// Storage model private Storage store = new Database(); /// UI model /// /// Must be subclass GUI to cover columns. private GUI ui = new GUI(); /// Application controller private Controller control = new Controller(store, ui, this); /// column state data as List of ActivityColumn objects private List columns; /// Application instantiation /// /// @param args application parameters public static void main(final String[] args) { launch(args); } @Override public void start(final Stage stage) throws IOException { // pass application parameters to controller control.setParameters(getParameters().getRaw()); // clear old insertions into participation table control.initialize(); // define list of columns based on their names columns = List.of( new ActivityColumn(GUI.Section.PROGRAM), new ActivityColumn(GUI.Section.SUBJECT1), new ActivityColumn(GUI.Section.SUBJECT2), new ActivityColumn(GUI.Section.ELECTIVE) ); // define button functionality for each activity column for (ActivityColumn col : columns) { col.nameLabel.setStyle(LABEL_STYLE); col.ectsLabel.setStyle(LABEL_STYLE); col.categoryCombo.setPrefSize( LIST_WIDTH, LIST_HEIGHT); col.activitySelect.setPrefSize( LIST_WIDTH, LIST_HEIGHT); col.area.setPrefWidth(LIST_WIDTH); // all boxes share same activity logic col.activitySelect.setOnAction(event -> { control.onActivitySelected( col.categoryCombo, col.activitySelect.getValue(), col.area); control.updateEcts( col.ectsLabel, col.categoryCombo); }); // handle each category box switch (col.section) { case GUI.Section.PROGRAM -> { col.categoryCombo.getItems().addAll( "HumTek", "NatBach"); col.categoryCombo.setOnAction(event -> { control.onCategorySelected( col.section, col.categoryCombo.getValue()); }); } // TODO: use the list for filling the box case GUI.Section.SUBJECT1 -> { col.categoryCombo.getItems().addAll( "Computer Science", "Informatik", "Astrology"); col.categoryCombo.setOnAction(event -> { control.onSubjectModuleSelected( col.categoryCombo, columns.get( GUI.Section.SUBJECT2.column) .categoryCombo); control.onCategorySelected( col.section, col.categoryCombo.getValue()); }); } case GUI.Section.SUBJECT2 -> { col.categoryCombo.getItems().addAll( "Computer Science", "Informatik", "Astrology"); // TODO: figure out a better way... col.categoryCombo.setOnAction(event -> { control.onSubjectModuleSelected( col.categoryCombo, columns.get( GUI.Section.SUBJECT1.column) .categoryCombo); control.onCategorySelected( col.section, col.categoryCombo.getValue()); }); } case GUI.Section.ELECTIVE -> { // hide useless box col.categoryCombo.setVisible(false); control.fillElective(col.activitySelect); } } } // define HBox and scene for columns HBox root = new HBox( columns.get(GUI.Section.PROGRAM.column) .asVBox(), columns.get(GUI.Section.SUBJECT1.column) .asVBox(), columns.get(GUI.Section.SUBJECT2.column) .asVBox(), columns.get(GUI.Section.ELECTIVE.column) .asVBox()); Scene scene = new Scene( root, WINDOW_WIDTH, WINDOW_HEIGHT); stage.setTitle("Bachelorizer - RUC Course Selector"); stage.setScene(scene); stage.show(); } /// column of activities /// /// @param section structural section for column /// @param nameLabel display text /// @param categoryCombo dropdown list for categories /// @param activitySelect dropdown list for activities /// @param area description of chosen activities /// @param ectsLabel text to display ECTS points private record ActivityColumn( GUI.Section section, Label nameLabel, ComboBox categoryCombo, ComboBox activitySelect, TextArea area, Label ectsLabel ) { /// column of activities /// /// @param section structural section for column ActivityColumn(final GUI.Section section) { this( section, new Label(section.label), new ComboBox<>(), new ComboBox<>(), new TextArea(), new Label() ); } /// activity columne as VBox /// /// @return column of activities as VBox VBox asVBox() { return new VBox( nameLabel, categoryCombo, activitySelect, area, ectsLabel); } } /// populate activities for a category /// /// @param section structural section to operate on /// @param activities activities to apply as string public void setOptions( final GUI.Section section, final List activities ) { // clear the activity selection box columns.get(section.column).activitySelect .getItems().clear(); // fill activity box from data in store columns.get(section.column).activitySelect .getItems().addAll(activities); } /// remove selections from a category /// /// @param section structural section to operate on public void clearSelections(final GUI.Section section) { // clear text area columns.get(section.column).area.clear(); } }