diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e45147d..960ac00 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,21 +11,31 @@
UTF-8
-5.10.2
+ 5.10.2
+
+ 21
+ 21.0.4
+
org.openjfx
javafx-controls
- 21
+ ${javafx.version}
org.openjfx
javafx-fxml
- 21
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-media
+ ${javafx.version}
-
+
+
org.junit.jupiter
junit-jupiter-api
${junit.version}
@@ -36,39 +46,30 @@
junit-jupiter-engine
${junit.version}
test
-
+
+
+
org.apache.maven.plugins
maven-compiler-plugin
3.13.0
- 23
- 23
+ ${maven.compiler.release}
+
org.openjfx
javafx-maven-plugin
0.0.8
-
-
-
- default-cli
-
- sbu.javafx/sbu.javafx.MusicApp
- app
- app
- app
- true
- true
- true
-
-
-
+
+ sbu.javafx/sbu.javafx.MusicApp
+
+
\ 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..6351bdc 100644
--- a/src/main/java/sbu/javafx/Launcher.java
+++ b/src/main/java/sbu/javafx/Launcher.java
@@ -2,7 +2,10 @@ package sbu.javafx;
import javafx.application.Application;
-public class Launcher{
- public static void main(String[] args) {
- // TODO: Launch the JavaFX application by calling Application.launch(Main.class) }
-}
+public class Launcher
+{
+ public static void main(String[] args)
+ {
+ Application.launch(MusicApp.class, args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/sbu/javafx/MusicApp.java b/src/main/java/sbu/javafx/MusicApp.java
index a50badc..9810d80 100644
--- a/src/main/java/sbu/javafx/MusicApp.java
+++ b/src/main/java/sbu/javafx/MusicApp.java
@@ -7,8 +7,8 @@ import javafx.stage.Stage;
import java.io.IOException;
-public class MusicApp extends Application {
-
+public class MusicApp extends Application
+{
@Override
public void start(Stage stage) throws IOException
{
@@ -16,8 +16,7 @@ public class MusicApp extends Application {
Scene scene = new Scene(fxmlLoader.load());
stage.setTitle("Music Player");
stage.setScene(scene);
- //TODO: add icon to the stage
+ stage.setResizable(false);
stage.show();
}
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/sbu/javafx/MusicController.java b/src/main/java/sbu/javafx/MusicController.java
index 5da27a9..9dac1a9 100644
--- a/src/main/java/sbu/javafx/MusicController.java
+++ b/src/main/java/sbu/javafx/MusicController.java
@@ -1,31 +1,384 @@
package sbu.javafx;
import javafx.fxml.FXML;
-import javafx.event.ActionEvent;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
import javafx.scene.control.Label;
-import javafx.scene.input.MouseEvent;
+import javafx.scene.control.ListView;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
+import javafx.scene.media.Media;
+import javafx.scene.media.MediaPlayer;
+import javafx.stage.Stage;
-public class MusicController {
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
- // TODO: Define your UI components using the @FXML annotation.
+public class MusicController
+{
+ @FXML private VBox rootPane;
+ @FXML private Label titleLabel;
+ @FXML private Label nowPlayingLabel;
- //TODO: Implement the logic to play a song. This method should update the UI to show which song is currently playing.
+ @FXML private Button playBtn1;
+ @FXML private Button playBtn2;
+ @FXML private Button playBtn3;
+
+ @FXML private ImageView playIcon1;
+ @FXML private ImageView playIcon2;
+ @FXML private ImageView playIcon3;
+
+ @FXML private Button stopBtn1;
+ @FXML private Button stopBtn2;
+ @FXML private Button stopBtn3;
+
+ private final List playlist = new ArrayList<>();
+ private MediaPlayer mediaPlayer;
+
+ private boolean darkTheme = false;
+
+ private String currentPlayingSongFile = null;
+ private boolean isPlaying = false;
+
+ private boolean isSong1Playing = false;
+ private boolean isSong2Playing = false;
+ private boolean isSong3Playing = false;
+
+
+ private Image playImage;
+ private Image pauseImage;
+ private Image stopImage;
+
+ private final String song1 = "Careless Whisper";
+ private final String song2 = "Adore You";
+ private final String song3 = "Wildflower";
+
+ private final String song1File = "/sbu/javafx/audio/careless_whisper.mp3";
+ private final String song2File = "/sbu/javafx/audio/adore_you.mp3";
+ private final String song3File = "/sbu/javafx/audio/wildflower.mp3";
@FXML
- public void handlePlayAction() {
- //Update labels, change button icons, etc.
+ private void initialize()
+ {
+ playImage = loadImage("/sbu/javafx/images/play.png");
+ pauseImage = loadImage("/sbu/javafx/images/pause.png");
+ stopImage = loadImage("/sbu/javafx/images/stop.png");
+
+ updateAllButtonIcons();
+ updateNowPlaying("None");
+ applyLightTheme();
}
- //TODO: Logic to add a specific song to the user's playlist.
+ @FXML public void handlePlay1() { togglePlay(song1, song1File, playIcon1, 1); }
+ @FXML public void handlePlay2() { togglePlay(song2, song2File, playIcon2, 2); }
+ @FXML public void handlePlay3() { togglePlay(song3, song3File, playIcon3, 3); }
- public void addToPlaylist(String songName) {
- //Add the song to a list or update a ListView.
+ @FXML public void handleStop1() { stopSong(1); }
+ @FXML public void handleStop2() { stopSong(2); }
+ @FXML public void handleStop3() { stopSong(3); }
+
+ @FXML public void handleAdd1() { addToPlaylist(song1); }
+ @FXML public void handleAdd2() { addToPlaylist(song2); }
+ @FXML public void handleAdd3() { addToPlaylist(song3); }
+
+ @FXML
+ public void handleViewPlaylist()
+ {
+ Stage stage = new Stage();
+ VBox root = new VBox(10);
+ root.setStyle("-fx-padding: 16; -fx-background-color: white;");
+ Label title = new Label("My Playlist");
+ title.setStyle("-fx-font-size: 18px; -fx-font-weight: bold;");
+ ListView listView = new ListView<>();
+ listView.getItems().addAll(playlist);
+ root.getChildren().addAll(title, listView);
+ Scene scene = new Scene(root, 300, 400);
+ stage.setScene(scene);
+ stage.setTitle("Playlist");
+ stage.show();
}
- // TODO:This method should open a new window or change the current scene to display the "Playlist" page.
-
- public void openPlaylistWindow() {
- //Create a new stage and show the playlist UI.
+ @FXML
+ public void handleToggleTheme()
+ {
+ darkTheme = !darkTheme;
+ if (darkTheme) applyDarkTheme(); else applyLightTheme();
}
-}
+
+ private void togglePlay(String songName, String resourcePath, ImageView icon, int songIndex)
+ {
+ boolean shouldPlay = false;
+
+ switch(songIndex)
+ {
+ case 1: shouldPlay = !isSong1Playing; break;
+ case 2: shouldPlay = !isSong2Playing; break;
+ case 3: shouldPlay = !isSong3Playing; break;
+ }
+
+ if (shouldPlay)
+ {
+ playOrResumeSong(songName, resourcePath, icon, songIndex);
+ }
+ else
+ {
+ pauseSong(songIndex);
+ }
+ updateAllButtonIcons();
+ }
+
+ private void playOrResumeSong(String songName, String resourcePath, ImageView icon, int songIndex)
+ {
+ try
+ {
+ if (mediaPlayer != null && isPlaying && !resourcePath.equals(currentPlayingSongFile))
+ {
+ stopPlayback();
+ updateAllButtonIcons();
+ }
+
+ if (mediaPlayer != null && !isPlaying && resourcePath.equals(currentPlayingSongFile))
+ {
+ mediaPlayer.play();
+ isPlaying = true;
+ }
+ else
+ {
+ if (mediaPlayer != null)
+ {
+ mediaPlayer.stop();
+ mediaPlayer.dispose();
+ }
+
+ URL url = getClass().getResource(resourcePath);
+ if (url == null)
+ {
+ updateNowPlaying(songName + " (file not found)");
+ setSongStatus(songIndex, false);
+ return;
+ }
+
+ Media media = new Media(url.toExternalForm());
+ mediaPlayer = new MediaPlayer(media);
+
+ currentPlayingSongFile = resourcePath;
+ isPlaying = true;
+
+ mediaPlayer.setOnEndOfMedia(() ->
+ {
+ stopPlayback();
+ updateNowPlaying("None");
+ setSongStatus(songIndex, false);
+ updateAllButtonIcons();
+ });
+
+ mediaPlayer.setOnError(() ->
+ {
+ stopPlayback();
+ updateNowPlaying(songName + " (error)");
+ setSongStatus(songIndex, false);
+ updateAllButtonIcons();
+ });
+
+ mediaPlayer.play();
+ }
+
+ setSongStatus(songIndex, true);
+ updateNowPlaying(songName);
+
+ }
+ catch (Exception e)
+ {
+ stopPlayback();
+ updateNowPlaying(songName + " (error)");
+ setSongStatus(songIndex, false);
+ e.printStackTrace();
+ }
+ }
+
+ private void pauseSong(int songIndex)
+ {
+ if (mediaPlayer != null && isPlaying && currentPlayingSongFile != null)
+ {
+ String targetFile = "";
+ switch(songIndex)
+ {
+ case 1: targetFile = song1File; break;
+ case 2: targetFile = song2File; break;
+ case 3: targetFile = song3File; break;
+ }
+
+ if (currentPlayingSongFile.equals(targetFile))
+ {
+ mediaPlayer.pause();
+ isPlaying = false;
+ setSongStatus(songIndex, false);
+ updateNowPlaying("Paused: " + getSongNameByIndex(songIndex));
+ updateAllButtonIcons();
+ }
+ }
+ }
+
+ private void stopSong(int songIndex)
+ {
+ if (mediaPlayer != null && isPlaying)
+ {
+ String targetFile = "";
+ switch(songIndex)
+ {
+ case 1: targetFile = song1File; break;
+ case 2: targetFile = song2File; break;
+ case 3: targetFile = song3File; break;
+ }
+
+ if (currentPlayingSongFile != null && currentPlayingSongFile.equals(targetFile))
+ {
+ mediaPlayer.stop();
+ mediaPlayer.dispose();
+ mediaPlayer = null;
+ isPlaying = false;
+ currentPlayingSongFile = null;
+ setSongStatus(songIndex, false);
+ updateNowPlaying("None");
+ updateAllButtonIcons();
+ }
+ else
+ {
+ setSongStatus(songIndex, false);
+ updateAllButtonIcons();
+ }
+ }
+ else
+ {
+ setSongStatus(songIndex, false);
+ updateAllButtonIcons();
+ }
+ }
+
+
+ private void stopPlayback()
+ {
+ try
+ {
+ if (mediaPlayer != null)
+ {
+ mediaPlayer.stop();
+ mediaPlayer.dispose();
+ mediaPlayer = null;
+ }
+ }
+ catch (Exception ignored) { }
+
+ isPlaying = false;
+ currentPlayingSongFile = null;
+ isSong1Playing = false;
+ isSong2Playing = false;
+ isSong3Playing = false;
+ }
+
+ private void setSongStatus(int songIndex, boolean status)
+ {
+ switch(songIndex)
+ {
+ case 1: isSong1Playing = status; break;
+ case 2: isSong2Playing = status; break;
+ case 3: isSong3Playing = status; break;
+ }
+ }
+
+ private boolean getSongStatus(int songIndex)
+ {
+ switch(songIndex)
+ {
+ case 1: return isSong1Playing;
+ case 2: return isSong2Playing;
+ case 3: return isSong3Playing;
+ default: return false;
+ }
+ }
+
+ private String getSongNameByIndex(int songIndex)
+ {
+ switch(songIndex)
+ {
+ case 1: return song1;
+ case 2: return song2;
+ case 3: return song3;
+ default: return "Unknown";
+ }
+ }
+
+ public void addToPlaylist(String songName)
+ {
+ if (!playlist.contains(songName))
+ {
+ playlist.add(songName);
+ System.out.println(songName + " added to playlist.");
+ }
+ else
+ {
+ System.out.println(songName + " is already in the playlist.");
+ }
+ }
+
+ private void updateNowPlaying(String text)
+ {
+ if (nowPlayingLabel != null) {nowPlayingLabel.setText("Now Playing: " + text);}
+ }
+
+ private void updateAllButtonIcons()
+ {
+ updateIconButton(playIcon1, isSong1Playing, song1File);
+ updateIconButton(playIcon2, isSong2Playing, song2File);
+ updateIconButton(playIcon3, isSong3Playing, song3File);
+ }
+
+ private void updateIconButton(ImageView icon, boolean isPlayingCurrent, String currentSongFile)
+ {
+ if (icon == null) return;
+
+ if (isPlaying && currentPlayingSongFile != null && currentPlayingSongFile.equals(currentSongFile))
+ {
+ if (icon.getImage() != pauseImage) {icon.setImage(pauseImage);}
+ }
+ else
+ {
+ if (icon.getImage() != playImage) {icon.setImage(playImage);}
+ }
+ }
+
+ private Image loadImage(String resourcePath)
+ {
+ try
+ {
+ URL url = getClass().getResource(resourcePath);
+ if (url == null)
+ {
+ System.err.println("Image not found: " + resourcePath);
+ return null;
+ }
+ return new Image(url.toExternalForm());
+ }
+ catch (Exception e)
+ {
+ System.err.println("Error loading image: " + resourcePath + " - " + e.getMessage());
+ return null;
+ }
+ }
+
+ private void applyDarkTheme()
+ {
+ if (rootPane != null) rootPane.setStyle("-fx-padding: 24; -fx-background-color: #121212;");
+ if (titleLabel != null) titleLabel.setStyle("-fx-font-size: 26px; -fx-font-weight: bold; -fx-text-fill: white;");
+ if (nowPlayingLabel != null) nowPlayingLabel.setStyle("-fx-padding: 12;" + "-fx-background-color: #1E1E1E;" + "-fx-background-radius: 10;" + "-fx-border-color: #444444;" + "-fx-border-radius: 10;" + "-fx-text-fill: white;");
+ }
+
+ private void applyLightTheme()
+ {
+ if (rootPane != null) rootPane.setStyle("-fx-padding: 24; -fx-background-color: #F8F8F8;");
+ if (titleLabel != null) titleLabel.setStyle("-fx-font-size: 26px; -fx-font-weight: bold; -fx-text-fill: #222222;");
+ if (nowPlayingLabel != null) nowPlayingLabel.setStyle("-fx-padding: 12;" + "-fx-background-color: white;" + "-fx-background-radius: 10;" + "-fx-border-color: rgba(0,0,0,0.15);" + "-fx-border-radius: 10;" + "-fx-text-fill: black;");
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/sbu/javafx/App-view.fxml b/src/main/resources/sbu/javafx/App-view.fxml
index 306c8d3..a57e3f4 100644
--- a/src/main/resources/sbu/javafx/App-view.fxml
+++ b/src/main/resources/sbu/javafx/App-view.fxml
@@ -1,8 +1,228 @@
+
+
+
+
+
+
+
+
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/sbu/javafx/audio/adore_you.mp3 b/src/main/resources/sbu/javafx/audio/adore_you.mp3
new file mode 100644
index 0000000..274dac2
Binary files /dev/null and b/src/main/resources/sbu/javafx/audio/adore_you.mp3 differ
diff --git a/src/main/resources/sbu/javafx/audio/careless_whisper.mp3 b/src/main/resources/sbu/javafx/audio/careless_whisper.mp3
new file mode 100644
index 0000000..aaa81e6
Binary files /dev/null and b/src/main/resources/sbu/javafx/audio/careless_whisper.mp3 differ
diff --git a/src/main/resources/sbu/javafx/audio/wildflower.mp3 b/src/main/resources/sbu/javafx/audio/wildflower.mp3
new file mode 100644
index 0000000..4085864
Binary files /dev/null and b/src/main/resources/sbu/javafx/audio/wildflower.mp3 differ
diff --git a/src/main/resources/sbu/javafx/css/dark.css b/src/main/resources/sbu/javafx/css/dark.css
new file mode 100644
index 0000000..fc1b35e
--- /dev/null
+++ b/src/main/resources/sbu/javafx/css/dark.css
@@ -0,0 +1,15 @@
+.root
+{
+ -fx-background-color: #1e1e1e;
+}
+
+.label
+{
+ -fx-text-fill: white;
+}
+
+.button
+{
+ -fx-background-color: #444;
+ -fx-text-fill: white;
+}
\ No newline at end of file
diff --git a/src/main/resources/sbu/javafx/css/light.css b/src/main/resources/sbu/javafx/css/light.css
new file mode 100644
index 0000000..065037d
--- /dev/null
+++ b/src/main/resources/sbu/javafx/css/light.css
@@ -0,0 +1,15 @@
+.root
+{
+ -fx-background-color: white;
+}
+
+.label
+{
+ -fx-text-fill: black;
+}
+
+.button
+{
+ -fx-background-color: #e0e0e0;
+ -fx-text-fill: black;
+}
\ No newline at end of file
diff --git a/src/main/resources/sbu/javafx/images/add.png b/src/main/resources/sbu/javafx/images/add.png
new file mode 100644
index 0000000..10691a8
Binary files /dev/null and b/src/main/resources/sbu/javafx/images/add.png differ
diff --git a/src/main/resources/sbu/javafx/images/pause.png b/src/main/resources/sbu/javafx/images/pause.png
new file mode 100644
index 0000000..11c7efd
Binary files /dev/null and b/src/main/resources/sbu/javafx/images/pause.png differ
diff --git a/src/main/resources/sbu/javafx/images/play.png b/src/main/resources/sbu/javafx/images/play.png
new file mode 100644
index 0000000..b49f598
Binary files /dev/null and b/src/main/resources/sbu/javafx/images/play.png differ
diff --git a/src/main/resources/sbu/javafx/images/playlist.png b/src/main/resources/sbu/javafx/images/playlist.png
new file mode 100644
index 0000000..f409e15
Binary files /dev/null and b/src/main/resources/sbu/javafx/images/playlist.png differ
diff --git a/src/main/resources/sbu/javafx/images/stop.png b/src/main/resources/sbu/javafx/images/stop.png
new file mode 100644
index 0000000..8044083
Binary files /dev/null and b/src/main/resources/sbu/javafx/images/stop.png differ
diff --git a/src/main/resources/sbu/javafx/images/theme.png b/src/main/resources/sbu/javafx/images/theme.png
new file mode 100644
index 0000000..ed7e928
Binary files /dev/null and b/src/main/resources/sbu/javafx/images/theme.png differ