From 14398f709afaadc6244e1f7283677154e8a52b6c Mon Sep 17 00:00:00 2001 From: Arefe Talebi Date: Wed, 15 Jul 2026 02:22:00 +0330 Subject: [PATCH] HW7 --- .idea/misc.xml | 2 +- .idea/vcs.xml | 6 + src/main/java/sbu/javafx/Launcher.java | 4 +- src/main/java/sbu/javafx/MusicApp.java | 1 + src/main/java/sbu/javafx/MusicController.java | 132 +++++++++++++++- src/main/resources/sbu/javafx/App-view.fxml | 40 ++++- src/main/resources/sbu/javafx/dark.css | 142 +++++++++++++++++ src/main/resources/sbu/javafx/light.css | 144 ++++++++++++++++++ 8 files changed, 462 insertions(+), 9 deletions(-) create mode 100644 .idea/vcs.xml create mode 100644 src/main/resources/sbu/javafx/dark.css create mode 100644 src/main/resources/sbu/javafx/light.css diff --git a/.idea/misc.xml b/.idea/misc.xml index 001e756..14641d0 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/java/sbu/javafx/Launcher.java b/src/main/java/sbu/javafx/Launcher.java index c288d10..3a7a58c 100644 --- a/src/main/java/sbu/javafx/Launcher.java +++ b/src/main/java/sbu/javafx/Launcher.java @@ -4,5 +4,7 @@ import javafx.application.Application; public class Launcher{ public static void main(String[] args) { - // TODO: Launch the JavaFX application by calling Application.launch(Main.class) } + // TODO: Launch the JavaFX application by calling Application.launch(Main.class) + Application.launch(MusicApp.class, args); + } } diff --git a/src/main/java/sbu/javafx/MusicApp.java b/src/main/java/sbu/javafx/MusicApp.java index a50badc..1c68815 100644 --- a/src/main/java/sbu/javafx/MusicApp.java +++ b/src/main/java/sbu/javafx/MusicApp.java @@ -14,6 +14,7 @@ public class MusicApp extends Application { { FXMLLoader fxmlLoader = new FXMLLoader(MusicApp.class.getResource("App-view.fxml")); Scene scene = new Scene(fxmlLoader.load()); + scene.getStylesheets().add(MusicApp.class.getResource("light.css").toExternalForm()); stage.setTitle("Music Player"); stage.setScene(scene); //TODO: add icon to the stage diff --git a/src/main/java/sbu/javafx/MusicController.java b/src/main/java/sbu/javafx/MusicController.java index 5da27a9..e73dfe5 100644 --- a/src/main/java/sbu/javafx/MusicController.java +++ b/src/main/java/sbu/javafx/MusicController.java @@ -2,30 +2,150 @@ package sbu.javafx; import javafx.fxml.FXML; import javafx.event.ActionEvent; -import javafx.scene.control.Label; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.*; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.VBox; +import javafx.scene.layout.*; +import javafx.stage.Modality; +import javafx.stage.Stage; + +import java.util.ArrayList; public class MusicController { // TODO: Define your UI components using the @FXML annotation. + @FXML private Label nowPlayingLabel; + @FXML private VBox songListVBox; + @FXML private Button themeToggleBtn; - //TODO: Implement the logic to play a song. This method should update the UI to show which song is currently playing. + private final ArrayList playlist = new ArrayList<>(); + private boolean darkMode = false; + private Button currentPlayingBtn = null; + private HBox currentPlayingRow = null; + + private static final String[][] SONGS = { + {"Blinding Lights", "The Weeknd", "3:20"}, + {"Shape of You", "Ed Sheeran", "3:53"}, + {"Levitating", "Dua Lipa", "3:23"}, + {"As It Was", "Harry Styles", "2:37"}, + {"Stay", "The Kid LAROI & J. Bieber", "2:21"}, + }; + @FXML + public void initialize() { + for (String[] song : SONGS) { + songListVBox.getChildren().add(buildSongRow(song[0], song[1], song[2])); + } + } + + private HBox buildSongRow(String title, String artist, String duration) { + Label titleLabel = new Label(title); + titleLabel.getStyleClass().add("song-title"); + + Label subtitleLabel = new Label(artist + " · " + duration); + subtitleLabel.getStyleClass().add("song-subtitle"); + + VBox infoBox = new VBox(3, titleLabel, subtitleLabel); + infoBox.setAlignment(Pos.CENTER_LEFT); + HBox.setHgrow(infoBox, Priority.ALWAYS); + + Button playBtn = new Button("▶ Play"); + playBtn.getStyleClass().add("play-btn"); + playBtn.setOnAction(e -> handlePlayAction(title, playBtn, (HBox) playBtn.getParent())); + + Button addBtn = new Button("+ Add"); + addBtn.getStyleClass().add("add-btn"); + addBtn.setOnAction(e -> addToPlaylist(title)); + + HBox row = new HBox(12, infoBox, playBtn, addBtn); + row.setAlignment(Pos.CENTER_LEFT); + row.setPadding(new Insets(12, 16, 12, 16)); + row.getStyleClass().add("song-row"); + + return row; + } + + //TODO: Implement the logic to play a song. This method should update the UI to show which song is currently playing. @FXML public void handlePlayAction() { - //Update labels, change button icons, etc. + + } + + public void handlePlayAction(String songTitle, Button playBtn, HBox row) { + if (currentPlayingRow != null) { + currentPlayingRow.getStyleClass().remove("song-row-playing"); + } + if (currentPlayingBtn != null) { + currentPlayingBtn.setText("▶ Play"); + currentPlayingBtn.getStyleClass().remove("play-btn-active"); + } + + row.getStyleClass().add("song-row-playing"); + playBtn.setText("♪ Playing"); + playBtn.getStyleClass().add("play-btn-active"); + + currentPlayingRow = row; + currentPlayingBtn = playBtn; + + nowPlayingLabel.setText("Now Playing: " + songTitle); } //TODO: Logic to add a specific song to the user's playlist. - public void addToPlaylist(String songName) { //Add the song to a list or update a ListView. + if (!playlist.contains(songName)) { + playlist.add(songName); + } } // TODO:This method should open a new window or change the current scene to display the "Playlist" page. - + @FXML public void openPlaylistWindow() { //Create a new stage and show the playlist UI. + Stage playlistStage = new Stage(); + playlistStage.initModality(Modality.APPLICATION_MODAL); + playlistStage.setTitle("Playlist"); + + Label header = new Label("Your Playlist"); + header.getStyleClass().add("playlist-header"); + + ListView listView = new ListView<>(); + if (playlist.isEmpty()) { + listView.getItems().add("No songs added yet."); + } else { + listView.getItems().addAll(playlist); + } + VBox.setVgrow(listView, Priority.ALWAYS); + + Button closeBtn = new Button("Close"); + closeBtn.setOnAction(e -> playlistStage.close()); + + VBox layout = new VBox(16, header, listView, closeBtn); + layout.setPadding(new Insets(24)); + layout.setAlignment(Pos.TOP_CENTER); + layout.getStyleClass().add("playlist-root"); + + Scene playlistScene = new Scene(layout, 400, 350); + String theme = darkMode ? "dark.css" : "light.css"; + playlistScene.getStylesheets().add(MusicApp.class.getResource(theme).toExternalForm()); + + playlistStage.setScene(playlistScene); + playlistStage.show(); + } + + @FXML + public void handleThemeToggle() { + darkMode = !darkMode; + Scene scene = themeToggleBtn.getScene(); + scene.getStylesheets().clear(); + if (darkMode) { + scene.getStylesheets().add(MusicApp.class.getResource("dark.css").toExternalForm()); + themeToggleBtn.setText("☀ Light Mode"); + } else { + scene.getStylesheets().add(MusicApp.class.getResource("light.css").toExternalForm()); + themeToggleBtn.setText("🌙 Dark Mode"); + } } } diff --git a/src/main/resources/sbu/javafx/App-view.fxml b/src/main/resources/sbu/javafx/App-view.fxml index 306c8d3..100022f 100644 --- a/src/main/resources/sbu/javafx/App-view.fxml +++ b/src/main/resources/sbu/javafx/App-view.fxml @@ -1,8 +1,46 @@ - + + + + + + + + + + + + + + diff --git a/src/main/resources/sbu/javafx/dark.css b/src/main/resources/sbu/javafx/dark.css new file mode 100644 index 0000000..2d5f0d5 --- /dev/null +++ b/src/main/resources/sbu/javafx/dark.css @@ -0,0 +1,142 @@ +/* ── Global ──────────────────────────────────────────────────────────── */ +.root { + -fx-background-color: #121212; + -fx-font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; +} +.content-area { + -fx-background-color: #121212; +} + +/* ── Header ──────────────────────────────────────────────────────────── */ +.header { + -fx-background-color: #1e1e1e; + -fx-border-color: #2c2c2c; + -fx-border-width: 0 0 1 0; + -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.4), 4, 0, 0, 2); +} +.app-title { + -fx-font-size: 22px; + -fx-font-weight: bold; + -fx-text-fill: #e0e0e0; +} + +/* ── Now-playing banner ──────────────────────────────────────────────── */ +.now-playing { + -fx-background-color: #1a2744; + -fx-border-color: #2a4a7f; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-padding: 10 16 10 16; + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: #90caf9; +} + +/* ── Song scroll pane ────────────────────────────────────────────────── */ +.song-scroll { + -fx-background-color: transparent; + -fx-border-color: transparent; +} +.song-scroll > .viewport { + -fx-background-color: transparent; +} + +/* ── Song row ────────────────────────────────────────────────────────── */ +.song-row { + -fx-background-color: #1e1e1e; + -fx-background-radius: 10; + -fx-border-color: #2c2c2c; + -fx-border-radius: 10; + -fx-border-width: 1; + -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 4, 0, 0, 1); +} +.song-row:hover { + -fx-background-color: #252535; + -fx-border-color: #3a3a5c; +} +.song-row-playing { + -fx-background-color: #1a2744; + -fx-border-color: #64b5f6; + -fx-border-width: 1.5; +} + +.song-title { + -fx-font-size: 15px; + -fx-font-weight: bold; + -fx-text-fill: #e0e0e0; +} +.song-subtitle { + -fx-font-size: 12px; + -fx-text-fill: #9e9e9e; +} + +/* ── Buttons ─────────────────────────────────────────────────────────── */ +.play-btn { + -fx-background-color: #1976d2; + -fx-text-fill: white; + -fx-font-weight: bold; + -fx-font-size: 12px; + -fx-background-radius: 6; + -fx-cursor: hand; + -fx-padding: 7 14 7 14; +} +.play-btn:hover { -fx-background-color: #1565c0; } +.play-btn-active { -fx-background-color: #388e3c; } +.play-btn-active:hover { -fx-background-color: #2e7d32; } + +.add-btn { + -fx-background-color: #2c2c2c; + -fx-text-fill: #cccccc; + -fx-font-size: 12px; + -fx-background-radius: 6; + -fx-cursor: hand; + -fx-padding: 7 14 7 14; +} +.add-btn:hover { -fx-background-color: #3a3a3a; } + +.playlist-btn { + -fx-background-color: #7b1fa2; + -fx-text-fill: white; + -fx-font-weight: bold; + -fx-font-size: 14px; + -fx-background-radius: 8; + -fx-cursor: hand; + -fx-padding: 12 0 12 0; +} +.playlist-btn:hover { -fx-background-color: #6a1b9a; } + +.theme-btn { + -fx-background-color: #e0a020; + -fx-text-fill: #1a1a1a; + -fx-font-size: 12px; + -fx-background-radius: 20; + -fx-cursor: hand; + -fx-padding: 6 14 6 14; +} +.theme-btn:hover { -fx-background-color: #c8860a; } + +/* ── Playlist window ─────────────────────────────────────────────────── */ +.playlist-root { -fx-background-color: #121212; } +.playlist-header { + -fx-font-size: 20px; + -fx-font-weight: bold; + -fx-text-fill: #e0e0e0; +} +.playlist-list { + -fx-background-color: #1e1e1e; + -fx-border-color: #2c2c2c; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-font-size: 14px; + -fx-control-inner-background: #1e1e1e; + -fx-text-fill: #e0e0e0; +} +.close-btn { + -fx-background-color: #c62828; + -fx-text-fill: white; + -fx-font-weight: bold; + -fx-background-radius: 6; + -fx-cursor: hand; + -fx-padding: 8 20 8 20; +} +.close-btn:hover { -fx-background-color: #b71c1c; } diff --git a/src/main/resources/sbu/javafx/light.css b/src/main/resources/sbu/javafx/light.css new file mode 100644 index 0000000..66372a5 --- /dev/null +++ b/src/main/resources/sbu/javafx/light.css @@ -0,0 +1,144 @@ +/* ── Global ──────────────────────────────────────────────────────────── */ +.root { + -fx-background-color: #f5f5f7; + -fx-font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; +} +.content-area { + -fx-background-color: #f5f5f7; +} + +/* ── Header ──────────────────────────────────────────────────────────── */ +.header { + -fx-background-color: #ffffff; + -fx-border-color: #e0e0e0; + -fx-border-width: 0 0 1 0; + -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.08), 4, 0, 0, 2); +} +.app-title { + -fx-font-size: 22px; + -fx-font-weight: bold; + -fx-text-fill: #1a1a2e; +} + +/* ── Now-playing banner ──────────────────────────────────────────────── */ +.now-playing { + -fx-background-color: #e8f4fd; + -fx-border-color: #b3d9f5; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-padding: 10 16 10 16; + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: #1565c0; +} + +/* ── Song scroll pane ────────────────────────────────────────────────── */ +.song-scroll { + -fx-background-color: transparent; + -fx-border-color: transparent; +} +.song-scroll > .viewport { + -fx-background-color: transparent; +} + +/* ── Song row ────────────────────────────────────────────────────────── */ +.song-row { + -fx-background-color: #ffffff; + -fx-background-radius: 10; + -fx-border-color: #e8e8e8; + -fx-border-radius: 10; + -fx-border-width: 1; + -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.06), 4, 0, 0, 1); +} +.song-row:hover { + -fx-background-color: #f0f7ff; + -fx-border-color: #b3d9f5; +} +/* Bonus – highlight active row */ +.song-row-playing { + -fx-background-color: #e3f2fd; + -fx-border-color: #1565c0; + -fx-border-width: 1.5; +} + +.song-title { + -fx-font-size: 15px; + -fx-font-weight: bold; + -fx-text-fill: #1a1a2e; +} +.song-subtitle { + -fx-font-size: 12px; + -fx-text-fill: #6b7280; +} + +/* ── Buttons ─────────────────────────────────────────────────────────── */ +.play-btn { + -fx-background-color: #1565c0; + -fx-text-fill: white; + -fx-font-weight: bold; + -fx-font-size: 12px; + -fx-background-radius: 6; + -fx-cursor: hand; + -fx-padding: 7 14 7 14; +} +.play-btn:hover { -fx-background-color: #0d47a1; } +/* Bonus – active play button */ +.play-btn-active { + -fx-background-color: #2e7d32; +} +.play-btn-active:hover { -fx-background-color: #1b5e20; } + +.add-btn { + -fx-background-color: #f0f0f0; + -fx-text-fill: #444; + -fx-font-size: 12px; + -fx-background-radius: 6; + -fx-cursor: hand; + -fx-padding: 7 14 7 14; +} +.add-btn:hover { -fx-background-color: #e0e0e0; } + +.playlist-btn { + -fx-background-color: #7b1fa2; + -fx-text-fill: white; + -fx-font-weight: bold; + -fx-font-size: 14px; + -fx-background-radius: 8; + -fx-cursor: hand; + -fx-padding: 12 0 12 0; +} +.playlist-btn:hover { -fx-background-color: #6a1b9a; } + +.theme-btn { + -fx-background-color: #37474f; + -fx-text-fill: white; + -fx-font-size: 12px; + -fx-background-radius: 20; + -fx-cursor: hand; + -fx-padding: 6 14 6 14; +} +.theme-btn:hover { -fx-background-color: #263238; } + +/* ── Playlist window ─────────────────────────────────────────────────── */ +.playlist-root { -fx-background-color: #f5f5f7; } +.playlist-header { + -fx-font-size: 20px; + -fx-font-weight: bold; + -fx-text-fill: #1a1a2e; +} +.playlist-list { + -fx-background-color: #ffffff; + -fx-border-color: #e0e0e0; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-font-size: 14px; +} +.close-btn { + -fx-background-color: #e53935; + -fx-text-fill: white; + -fx-font-weight: bold; + -fx-background-radius: 6; + -fx-cursor: hand; + -fx-padding: 8 20 8 20; +} +.close-btn:hover { -fx-background-color: #c62828; }