Sidebar UI updated.

This commit is contained in:
Asal Lotfi
2025-08-13 10:41:23 +03:30
parent 054f3db155
commit 1839dccd83
2 changed files with 126 additions and 63 deletions
@@ -17,10 +17,9 @@ import java.net.URL;
public class SidebarMenuController { public class SidebarMenuController {
private static final String CSS_PATH = "/org/to/telegramfinalproject/CSS/";
private static final String ICON_PATH = "/org/to/telegramfinalproject/Icons/"; private static final String ICON_PATH = "/org/to/telegramfinalproject/Icons/";
@FXML private VBox sidebarRoot; // fx:id must match FXML @FXML private VBox sidebarRoot;
@FXML private ImageView profileImage; @FXML private ImageView profileImage;
@FXML private Label usernameLabel; @FXML private Label usernameLabel;
@@ -38,33 +37,43 @@ public class SidebarMenuController {
@FXML private Region toggleThumb; @FXML private Region toggleThumb;
@FXML private ImageView nightModeIcon; @FXML private ImageView nightModeIcon;
private boolean nightModeOn = false; // start light by default // convenience handle
private final ThemeManager themeManager = ThemeManager.getInstance();
@FXML @FXML
public void initialize() { public void initialize() {
// Load default profile picture (if present) // Load default profile image (if present)
Image profile = loadImage("/org/to/telegramfinalproject/Images/profile.png"); Image profile = loadImage("/org/to/telegramfinalproject/Images/profile.png");
if (profile != null) profileImage.setImage(profile); if (profile != null) profileImage.setImage(profile);
// Set up handlers and defer theme application until the scene is available
setupButtonActions(); setupButtonActions();
setupToggleAction(); setupToggleAction();
// When the scene is set, apply the initial theme (light) and initialize icons/positions // When the Scene is ready:
sidebarRoot.sceneProperty().addListener((obs, oldScene, newScene) -> { sidebarRoot.sceneProperty().addListener((obs, oldScene, newScene) -> {
if (newScene != null) { if (newScene != null) {
// Apply light theme at startup // 1) Register with ThemeManager so this scene auto-updates on theme changes
updateTheme(false); themeManager.registerScene(newScene);
// Position the thumb after layout pass (to use actual widths) // 2) Make sure we start in LIGHT mode (if that's what you want)
Platform.runLater(() -> { // (If you already set the initial mode elsewhere, remove the next line)
double initialX = nightModeOn themeManager.setDarkMode(false);
? nightModeToggle.getWidth() - toggleThumb.getWidth() - 4
: 2; // 3) Sync icons & toggle with current mode
toggleThumb.setTranslateX(initialX); boolean dark = themeManager.isDarkMode();
}); updateIcons(dark);
syncToggleVisual(dark, /*animate=*/true);
// Ensure the thumb is correctly positioned after layout pass
Platform.runLater(() -> syncToggleVisual(themeManager.isDarkMode(), false));
} }
}); });
// If theme is changed from somewhere else (another screen), keep sidebar in sync
themeManager.darkModeProperty().addListener((o, wasDark, isDark) -> {
updateIcons(isDark);
syncToggleVisual(isDark, /*animate=*/true);
});
} }
private void setupButtonActions() { private void setupButtonActions() {
@@ -80,58 +89,22 @@ public class SidebarMenuController {
private void setupToggleAction() { private void setupToggleAction() {
nightModeToggle.setOnMouseClicked(e -> { nightModeToggle.setOnMouseClicked(e -> {
nightModeOn = !nightModeOn; boolean newDark = !themeManager.isDarkMode();
updateTheme(nightModeOn);
// animate thumb // Update the global theme via ThemeManager
double targetX = nightModeOn themeManager.setDarkMode(newDark);
? nightModeToggle.getWidth() - toggleThumb.getWidth() - 4
: 2; // Animate the thumb to its new position (listener will handle icons & final position sync)
TranslateTransition tt = new TranslateTransition(Duration.millis(200), toggleThumb); syncToggleVisual(newDark, /*animate=*/true);
tt.setToX(targetX);
tt.play();
}); });
} }
/** /** Update all button icons to match theme.
* Apply theme and swap icons. darkMode == true => apply dark theme (dark background), * darkMode == true => white icons => use *_light.png
* and use light (white) icons. darkMode == false => apply light theme and use dark icons. * darkMode == false => dark icons => use *_dark.png
*/ */
private void updateTheme(boolean darkMode) { private void updateIcons(boolean darkMode) {
Scene scene = sidebarRoot.getScene(); String suffix = darkMode ? "_light.png" : "_dark.png";
if (scene == null) return; // safety
// clear & apply the correct stylesheet
scene.getStylesheets().clear();
String cssFile = darkMode ? "dark_theme.css" : "light_theme.css";
URL cssUrl = getClass().getResource(CSS_PATH + cssFile);
if (cssUrl != null) {
scene.getStylesheets().add(cssUrl.toExternalForm());
} else {
System.err.println("CSS not found: " + CSS_PATH + cssFile);
}
// Keep toggle's "on" class in sync
if (darkMode) {
if (!nightModeToggle.getStyleClass().contains("on"))
nightModeToggle.getStyleClass().add("on");
} else {
nightModeToggle.getStyleClass().remove("on");
}
// Icon file suffix mapping:
// - For darkMode == true (dark background) we want white icons => use "_light.png"
// - For darkMode == false (light background) we want dark icons => use "_dark.png"
String iconSuffix = darkMode ? "_light.png" : "_dark.png";
setIcons(iconSuffix);
// night mode icon itself (moon)
Image moon = loadImage(ICON_PATH + "night_mode" + iconSuffix);
if (moon != null) nightModeIcon.setImage(moon);
}
private void setIcons(String suffix) {
myProfileButton.setGraphic(makeIcon(ICON_PATH + "my_profile" + suffix)); myProfileButton.setGraphic(makeIcon(ICON_PATH + "my_profile" + suffix));
newGroupButton.setGraphic(makeIcon(ICON_PATH + "new_group" + suffix)); newGroupButton.setGraphic(makeIcon(ICON_PATH + "new_group" + suffix));
newChannelButton.setGraphic(makeIcon(ICON_PATH + "new_channel" + suffix)); newChannelButton.setGraphic(makeIcon(ICON_PATH + "new_channel" + suffix));
@@ -139,9 +112,41 @@ public class SidebarMenuController {
savedMessagesButton.setGraphic(makeIcon(ICON_PATH + "saved_messages" + suffix)); savedMessagesButton.setGraphic(makeIcon(ICON_PATH + "saved_messages" + suffix));
settingsButton.setGraphic(makeIcon(ICON_PATH + "settings" + suffix)); settingsButton.setGraphic(makeIcon(ICON_PATH + "settings" + suffix));
telegramFeaturesButton.setGraphic(makeIcon(ICON_PATH + "telegram_features" + suffix)); telegramFeaturesButton.setGraphic(makeIcon(ICON_PATH + "telegram_features" + suffix));
telegramQnAButton.setGraphic(makeIcon(ICON_PATH + "telegram_qna" + suffix)); // prefer safe filename telegramQnAButton.setGraphic(makeIcon(ICON_PATH + "telegram_qna" + suffix));
// Night-mode moon icon
Image moon = loadImage(ICON_PATH + "night_mode" + suffix);
if (moon != null) nightModeIcon.setImage(moon);
} }
/** Keep the toggles CSS class and thumb position in sync with the current mode. */
private void syncToggleVisual(boolean darkMode, boolean animate) {
// CSS class "on" on the track
if (darkMode) {
if (!nightModeToggle.getStyleClass().contains("on")) {
nightModeToggle.getStyleClass().add("on");
}
} else {
nightModeToggle.getStyleClass().remove("on");
}
// Compute target X for the thumb
double offX = 2;
double onX = Math.max(2, nightModeToggle.getWidth() - toggleThumb.getWidth() - 4);
double targetX = darkMode ? onX : offX;
if (animate) {
TranslateTransition tt = new TranslateTransition(Duration.millis(200), toggleThumb);
tt.setToX(targetX);
tt.play();
} else {
toggleThumb.setTranslateX(targetX);
}
}
// --- helpers -------------------------------------------------------------
private Image loadImage(String path) { private Image loadImage(String path) {
URL res = getClass().getResource(path); URL res = getClass().getResource(path);
if (res == null) { if (res == null) {
@@ -0,0 +1,58 @@
package org.to.telegramfinalproject.UI;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import java.util.ArrayList;
import java.util.List;
public class ThemeManager {
private static final ThemeManager instance = new ThemeManager();
private final BooleanProperty darkMode = new SimpleBooleanProperty(false);
private final List<Scene> registeredScenes = new ArrayList<>();
private ThemeManager() {
// Whenever darkMode changes, update all registered scenes
darkMode.addListener((obs, oldVal, newVal) -> applyThemeToAll());
}
public static ThemeManager getInstance() {
return instance;
}
public BooleanProperty darkModeProperty() {
return darkMode;
}
public boolean isDarkMode() {
return darkMode.get();
}
public void setDarkMode(boolean dark) {
darkMode.set(dark);
}
public void registerScene(Scene scene) {
if (!registeredScenes.contains(scene)) {
registeredScenes.add(scene);
applyTheme(scene); // Apply current theme immediately
}
}
private void applyThemeToAll() {
for (Scene scene : registeredScenes) {
applyTheme(scene);
}
}
private void applyTheme(Scene scene) {
scene.getStylesheets().clear();
if (isDarkMode()) {
scene.getStylesheets().add(getClass().getResource("/org/to/telegramfinalproject/CSS/dark_theme.css").toExternalForm());
} else {
scene.getStylesheets().add(getClass().getResource("/org/to/telegramfinalproject/CSS/light_theme.css").toExternalForm());
}
}
}