From fdaa5e79953fb6ffad50edcbca6854fb81822947 Mon Sep 17 00:00:00 2001 From: Omnicscient <115184891+Omnicscient@users.noreply.github.com> Date: Wed, 17 Dec 2025 20:03:55 +0300 Subject: [PATCH] Convert top navigation bar to sidebar with expandable schedule submenu --- .../se302/controller/MainController.java | 187 +++++++++++++++--- .../org/example/se302/css/application.css | 55 ++++++ .../org/example/se302/view/main-view.fxml | 75 ++++--- 3 files changed, 267 insertions(+), 50 deletions(-) diff --git a/src/main/java/org/example/se302/controller/MainController.java b/src/main/java/org/example/se302/controller/MainController.java index a0a9a2e..0afa452 100644 --- a/src/main/java/org/example/se302/controller/MainController.java +++ b/src/main/java/org/example/se302/controller/MainController.java @@ -1,35 +1,57 @@ package org.example.se302.controller; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.Node; import org.example.se302.service.DataManager; +import java.io.IOException; + /** * Main controller for the application window. - * Manages the TabPane, status bar, and theme toggling. + * Manages the sidebar navigation, content area, status bar, and theme toggling. */ public class MainController { @FXML - private TabPane mainTabPane; + private VBox sidebar; @FXML - private Tab importTab; + private StackPane contentArea; @FXML - private Tab studentsTab; + private Button importBtn; @FXML - private Tab coursesTab; + private Button studentsBtn; @FXML - private Tab classroomsTab; + private Button coursesBtn; @FXML - private Tab scheduleTab; + private Button classroomsBtn; + + @FXML + private Button scheduleBtn; + + @FXML + private VBox scheduleSubMenu; + + @FXML + private Button calendarBtn; + + @FXML + private Button studentScheduleBtn; + + @FXML + private Button courseScheduleBtn; + + @FXML + private Button classroomScheduleBtn; @FXML private Label statusLabel; @@ -39,36 +61,62 @@ public class MainController { private DataManager dataManager; private boolean isDarkMode = false; + private boolean scheduleMenuOpen = false; + + // Cached views + private Node importView; + private Node studentsView; + private Node coursesView; + private Node classroomsView; + private Node calendarView; + private Node studentScheduleView; + private Node courseScheduleView; + private Node classroomScheduleView; + + private Button activeButton; @FXML public void initialize() { dataManager = DataManager.getInstance(); - // Initially disable data tabs until import is complete - studentsTab.setDisable(true); - coursesTab.setDisable(true); - classroomsTab.setDisable(true); - scheduleTab.setDisable(true); + // Pre-load main views + importView = loadView("import-view.fxml"); + studentsView = loadView("students-view.fxml"); + coursesView = loadView("courses-view.fxml"); + classroomsView = loadView("classrooms-view.fxml"); + + // Pre-load schedule views to eliminate delay + calendarView = loadView("schedule-calendar-view.fxml"); + studentScheduleView = loadView("schedule-student-view.fxml"); + courseScheduleView = loadView("schedule-course-view.fxml"); + classroomScheduleView = loadView("schedule-classroom-view.fxml"); + + // Show import view initially + contentArea.getChildren().clear(); + contentArea.getChildren().add(importView); + + // Set import as active by default + setActiveButton(importBtn); updateStatusBar(); - // Listen for data changes to automatically enable tabs + // Listen for data changes to automatically enable buttons dataManager.getStudents().addListener( (javafx.collections.ListChangeListener) c -> { if (dataManager.hasData()) { - enableDataTabs(); + enableDataButtons(); } }); } /** - * Enable data tabs after successful import. + * Enable data buttons after successful import. */ - public void enableDataTabs() { - studentsTab.setDisable(false); - coursesTab.setDisable(false); - classroomsTab.setDisable(false); - scheduleTab.setDisable(false); + public void enableDataButtons() { + studentsBtn.setDisable(false); + coursesBtn.setDisable(false); + classroomsBtn.setDisable(false); + scheduleBtn.setDisable(false); updateStatusBar(); } @@ -87,12 +135,105 @@ public class MainController { } } + /** + * Set the active button style + */ + private void setActiveButton(Button button) { + if (activeButton != null) { + activeButton.getStyleClass().remove("sidebar-button-active"); + } + activeButton = button; + activeButton.getStyleClass().add("sidebar-button-active"); + } + + /** + * Show a view in the content area + */ + private void showView(Node view, Button button) { + contentArea.getChildren().clear(); + contentArea.getChildren().add(view); + setActiveButton(button); + } + + /** + * Load a view from FXML + */ + private Node loadView(String fxmlPath) { + try { + FXMLLoader loader = new FXMLLoader( + getClass().getResource("/org/example/se302/view/" + fxmlPath)); + return loader.load(); + } catch (IOException e) { + e.printStackTrace(); + return new Label("Error loading view: " + fxmlPath); + } + } + + @FXML + private void onShowImport() { + showView(importView, importBtn); + } + + @FXML + private void onShowStudents() { + showView(studentsView, studentsBtn); + } + + @FXML + private void onShowCourses() { + showView(coursesView, coursesBtn); + } + + @FXML + private void onShowClassrooms() { + showView(classroomsView, classroomsBtn); + } + + /** + * Toggle schedule submenu visibility and show Calendar View by default + */ + @FXML + private void onToggleScheduleMenu() { + if (!scheduleMenuOpen) { + // First time opening - show Calendar View and expand submenu + scheduleMenuOpen = true; + scheduleSubMenu.setVisible(true); + scheduleSubMenu.setManaged(true); + showView(calendarView, calendarBtn); + } else { + // Toggle submenu visibility + scheduleMenuOpen = false; + scheduleSubMenu.setVisible(false); + scheduleSubMenu.setManaged(false); + } + } + + @FXML + private void onShowCalendar() { + showView(calendarView, calendarBtn); + } + + @FXML + private void onShowStudentSchedule() { + showView(studentScheduleView, studentScheduleBtn); + } + + @FXML + private void onShowCourseSchedule() { + showView(courseScheduleView, courseScheduleBtn); + } + + @FXML + private void onShowClassroomSchedule() { + showView(classroomScheduleView, classroomScheduleBtn); + } + /** * Toggle between light and dark themes. */ @FXML private void onToggleTheme() { - var root = mainTabPane.getScene().getRoot(); + var root = contentArea.getScene().getRoot(); var styleClass = root.getStyleClass(); if (isDarkMode) { diff --git a/src/main/resources/org/example/se302/css/application.css b/src/main/resources/org/example/se302/css/application.css index 7b16e30..eeb7934 100644 --- a/src/main/resources/org/example/se302/css/application.css +++ b/src/main/resources/org/example/se302/css/application.css @@ -612,4 +612,59 @@ /* ===== Animations & Transitions ===== */ * { -fx-transition: all 0.2s ease-in-out; +} + +/* ===== Sidebar Navigation ===== */ +.sidebar-button { + -fx-background-color: transparent; + -fx-text-fill: rgba(255, 255, 255, 0.8); + -fx-font-size: 13px; + -fx-font-weight: 600; + -fx-padding: 12 16; + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-alignment: CENTER_LEFT; + -fx-cursor: hand; +} + +.sidebar-button:hover { + -fx-background-color: rgba(255, 255, 255, 0.1); + -fx-text-fill: white; +} + +.sidebar-button:disabled { + -fx-text-fill: rgba(255, 255, 255, 0.3); + -fx-cursor: default; +} + +.sidebar-button-active { + -fx-background-color: rgba(255, 255, 255, 0.15); + -fx-text-fill: white; + -fx-border-color: rgba(255, 255, 255, 0.3); + -fx-border-width: 0 0 0 3; +} + +/* Sidebar Sub-buttons (indented) */ +.sidebar-button-sub { + -fx-background-color: transparent; + -fx-text-fill: rgba(255, 255, 255, 0.7); + -fx-font-size: 12px; + -fx-font-weight: 500; + -fx-padding: 8 12; + -fx-background-radius: 6; + -fx-border-radius: 6; + -fx-alignment: CENTER_LEFT; + -fx-cursor: hand; +} + +.sidebar-button-sub:hover { + -fx-background-color: rgba(255, 255, 255, 0.08); + -fx-text-fill: white; +} + +.sidebar-button-sub.sidebar-button-active { + -fx-background-color: rgba(79, 70, 229, 0.3); + -fx-text-fill: white; + -fx-border-color: #6366F1; + -fx-border-width: 0 0 0 2; } \ No newline at end of file diff --git a/src/main/resources/org/example/se302/view/main-view.fxml b/src/main/resources/org/example/se302/view/main-view.fxml index c4e21de..d3e2373 100644 --- a/src/main/resources/org/example/se302/view/main-view.fxml +++ b/src/main/resources/org/example/se302/view/main-view.fxml @@ -24,34 +24,55 @@ - + + + + + + + +