mirror of
https://github.com/sabazadam/Se302.git
synced 2025-12-31 12:21:22 +00:00
Convert top navigation bar to sidebar with expandable schedule submenu
This commit is contained in:
@@ -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<org.example.se302.model.Student>) 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) {
|
||||
|
||||
@@ -613,3 +613,58 @@
|
||||
* {
|
||||
-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;
|
||||
}
|
||||
@@ -24,34 +24,55 @@
|
||||
</VBox>
|
||||
</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>
|
||||
<TabPane fx:id="mainTabPane" tabClosingPolicy="UNAVAILABLE">
|
||||
<!-- Import Data Tab -->
|
||||
<Tab fx:id="importTab" text="📁 Import Data">
|
||||
<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>
|
||||
<StackPane fx:id="contentArea">
|
||||
<!-- Views are loaded dynamically by controller -->
|
||||
</StackPane>
|
||||
</center>
|
||||
|
||||
<!-- Bottom: Status Bar -->
|
||||
|
||||
Reference in New Issue
Block a user