Convert top navigation bar to sidebar with expandable schedule submenu

This commit is contained in:
Omnicscient
2025-12-17 20:03:55 +03:00
parent ce556e92fd
commit fdaa5e7995
3 changed files with 267 additions and 50 deletions

View File

@@ -1,35 +1,57 @@
package org.example.se302.controller; package org.example.se302.controller;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.Tab; import javafx.scene.layout.StackPane;
import javafx.scene.control.TabPane; import javafx.scene.layout.VBox;
import javafx.scene.Node;
import org.example.se302.service.DataManager; import org.example.se302.service.DataManager;
import java.io.IOException;
/** /**
* Main controller for the application window. * 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 { public class MainController {
@FXML @FXML
private TabPane mainTabPane; private VBox sidebar;
@FXML @FXML
private Tab importTab; private StackPane contentArea;
@FXML @FXML
private Tab studentsTab; private Button importBtn;
@FXML @FXML
private Tab coursesTab; private Button studentsBtn;
@FXML @FXML
private Tab classroomsTab; private Button coursesBtn;
@FXML @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 @FXML
private Label statusLabel; private Label statusLabel;
@@ -39,36 +61,62 @@ public class MainController {
private DataManager dataManager; private DataManager dataManager;
private boolean isDarkMode = false; 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 @FXML
public void initialize() { public void initialize() {
dataManager = DataManager.getInstance(); dataManager = DataManager.getInstance();
// Initially disable data tabs until import is complete // Pre-load main views
studentsTab.setDisable(true); importView = loadView("import-view.fxml");
coursesTab.setDisable(true); studentsView = loadView("students-view.fxml");
classroomsTab.setDisable(true); coursesView = loadView("courses-view.fxml");
scheduleTab.setDisable(true); 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(); updateStatusBar();
// Listen for data changes to automatically enable tabs // Listen for data changes to automatically enable buttons
dataManager.getStudents().addListener( dataManager.getStudents().addListener(
(javafx.collections.ListChangeListener<org.example.se302.model.Student>) c -> { (javafx.collections.ListChangeListener<org.example.se302.model.Student>) c -> {
if (dataManager.hasData()) { if (dataManager.hasData()) {
enableDataTabs(); enableDataButtons();
} }
}); });
} }
/** /**
* Enable data tabs after successful import. * Enable data buttons after successful import.
*/ */
public void enableDataTabs() { public void enableDataButtons() {
studentsTab.setDisable(false); studentsBtn.setDisable(false);
coursesTab.setDisable(false); coursesBtn.setDisable(false);
classroomsTab.setDisable(false); classroomsBtn.setDisable(false);
scheduleTab.setDisable(false); scheduleBtn.setDisable(false);
updateStatusBar(); 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. * Toggle between light and dark themes.
*/ */
@FXML @FXML
private void onToggleTheme() { private void onToggleTheme() {
var root = mainTabPane.getScene().getRoot(); var root = contentArea.getScene().getRoot();
var styleClass = root.getStyleClass(); var styleClass = root.getStyleClass();
if (isDarkMode) { if (isDarkMode) {

View File

@@ -612,4 +612,59 @@
/* ===== Animations & Transitions ===== */ /* ===== Animations & Transitions ===== */
* { * {
-fx-transition: all 0.2s ease-in-out; -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;
} }

View File

@@ -24,34 +24,55 @@
</VBox> </VBox>
</top> </top>
<!-- Center: Main TabPane --> <!-- Left: Sidebar Navigation -->
<left>
<VBox fx:id="sidebar" spacing="5" minWidth="180" prefWidth="200"
style="-fx-background-color: linear-gradient(to bottom, #2c3e50, #1a252f); -fx-padding: 15;">
<padding>
<Insets top="10" right="10" bottom="10" left="10"/>
</padding>
<Button fx:id="importBtn" text="📁 Import Data" onAction="#onShowImport"
maxWidth="Infinity" styleClass="sidebar-button"/>
<Button fx:id="studentsBtn" text="👤 Students" onAction="#onShowStudents"
maxWidth="Infinity" styleClass="sidebar-button" disable="true"/>
<Button fx:id="coursesBtn" text="📚 Courses" onAction="#onShowCourses"
maxWidth="Infinity" styleClass="sidebar-button" disable="true"/>
<Button fx:id="classroomsBtn" text="🏛️ Classrooms" onAction="#onShowClassrooms"
maxWidth="Infinity" styleClass="sidebar-button" disable="true"/>
<Button fx:id="scheduleBtn" text="📅 Schedule" onAction="#onToggleScheduleMenu"
maxWidth="Infinity" styleClass="sidebar-button" disable="true"/>
<!-- Schedule Sub-menu (initially hidden) -->
<VBox fx:id="scheduleSubMenu" spacing="2" managed="false" visible="false">
<padding>
<Insets left="15"/>
</padding>
<Button fx:id="calendarBtn" text="📆 Calendar View" onAction="#onShowCalendar"
maxWidth="Infinity" styleClass="sidebar-button-sub"/>
<Button fx:id="studentScheduleBtn" text="👤 Student Schedule" onAction="#onShowStudentSchedule"
maxWidth="Infinity" styleClass="sidebar-button-sub"/>
<Button fx:id="courseScheduleBtn" text="📚 Course Schedule" onAction="#onShowCourseSchedule"
maxWidth="Infinity" styleClass="sidebar-button-sub"/>
<Button fx:id="classroomScheduleBtn" text="🏛️ Classroom Schedule" onAction="#onShowClassroomSchedule"
maxWidth="Infinity" styleClass="sidebar-button-sub"/>
</VBox>
<Region VBox.vgrow="ALWAYS"/>
<Label text="Navigation" style="-fx-text-fill: rgba(255,255,255,0.5); -fx-font-size: 10px;"/>
</VBox>
</left>
<!-- Center: Content Area -->
<center> <center>
<TabPane fx:id="mainTabPane" tabClosingPolicy="UNAVAILABLE"> <StackPane fx:id="contentArea">
<!-- Import Data Tab --> <!-- Views are loaded dynamically by controller -->
<Tab fx:id="importTab" text="📁 Import Data"> </StackPane>
<fx:include source="import-view.fxml"/>
</Tab>
<!-- Students Tab -->
<Tab fx:id="studentsTab" text="👤 Students">
<fx:include source="students-view.fxml"/>
</Tab>
<!-- Courses Tab -->
<Tab fx:id="coursesTab" text="📚 Courses">
<fx:include source="courses-view.fxml"/>
</Tab>
<!-- Classrooms Tab -->
<Tab fx:id="classroomsTab" text="🏛️ Classrooms">
<fx:include source="classrooms-view.fxml"/>
</Tab>
<!-- Schedule Views Tab -->
<Tab fx:id="scheduleTab" text="📅 Schedule Views">
<fx:include source="schedule-views.fxml"/>
</Tab>
</TabPane>
</center> </center>
<!-- Bottom: Status Bar --> <!-- Bottom: Status Bar -->