Merge pull request 'implement music player + JavaFx UI' (#1) from develop into main

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-07-17 18:27:20 +00:00
7 changed files with 500 additions and 27 deletions
+7 -4
View File
@@ -2,7 +2,10 @@ package sbu.javafx;
import javafx.application.Application; import javafx.application.Application;
public class Launcher{ public class Launcher
public static void main(String[] args) { {
// TODO: Launch the JavaFX application by calling Application.launch(Main.class) } public static void main(String[] args)
} {
Application.launch(MusicApp.class, args);
}
}
+4 -5
View File
@@ -4,7 +4,6 @@ import javafx.application.Application;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
public class MusicApp extends Application { public class MusicApp extends Application {
@@ -12,12 +11,12 @@ public class MusicApp extends Application {
@Override @Override
public void start(Stage stage) throws IOException public void start(Stage stage) throws IOException
{ {
FXMLLoader fxmlLoader = new FXMLLoader(MusicApp.class.getResource("App-view.fxml")); FXMLLoader loader = new FXMLLoader(MusicApp.class.getResource("App-view.fxml"));
Scene scene = new Scene(fxmlLoader.load()); Scene scene = new Scene(loader.load(), 1280, 900);
stage.setTitle("Music Player"); stage.setTitle("Music Player");
stage.setScene(scene); stage.setScene(scene);
//TODO: add icon to the stage stage.setResizable(false);
stage.show(); stage.show();
} }
} }
+220 -14
View File
@@ -1,31 +1,237 @@
package sbu.javafx; package sbu.javafx;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.event.ActionEvent; import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent; import javafx.scene.control.ToggleButton;
import javafx.scene.layout.VBox; import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.ArrayList;
public class MusicController { public class MusicController {
// TODO: Define your UI components using the @FXML annotation. @FXML private Label nowPlayingLabel;
@FXML private GridPane songsGrid;
@FXML private ToggleButton themeToggle;
//TODO: Implement the logic to play a song. This method should update the UI to show which song is currently playing. @FXML private Label song1Title;
@FXML private Label song1Artist;
@FXML private Label song1Duration;
@FXML private Label song2Title;
@FXML private Label song2Artist;
@FXML private Label song2Duration;
@FXML private Label song3Title;
@FXML private Label song3Artist;
@FXML private Label song3Duration;
@FXML private Button playBtn1;
@FXML private Button playBtn2;
@FXML private Button playBtn3;
private final ArrayList<Song> playlist = new ArrayList<>();
private final ArrayList<Label> songRows = new ArrayList<>();
private Song[] songs;
private Pane rootPane;
@FXML @FXML
public void handlePlayAction() { public void initialize()
//Update labels, change button icons, etc. {
songs = new Song[] {
new Song("Somewhere I Belong", "Linkin Park", "03:45"),
new Song("Let Down", "Radiohead", "04:12"),
new Song("Snuff", "Slipknot", "05:08")
};
songRows.add(song1Title);
songRows.add(song2Title);
songRows.add(song3Title);
setActiveRow(-1);
} }
//TODO: Logic to add a specific song to the user's playlist. // -----------------------------
// Playlist actions
public void addToPlaylist(String songName) { // -----------------------------
//Add the song to a list or update a ListView. @FXML
private void addSong1ToPlaylist() {
addToPlaylist(songs[0]);
} }
// TODO:This method should open a new window or change the current scene to display the "Playlist" page. @FXML
private void addSong2ToPlaylist() {
addToPlaylist(songs[1]);
}
public void openPlaylistWindow() { @FXML
//Create a new stage and show the playlist UI. private void addSong3ToPlaylist() {
addToPlaylist(songs[2]);
}
private void addToPlaylist(Song song)
{
if (!playlist.contains(song))
playlist.add(song);
}
// -----------------------------
// Play actions
// -----------------------------
@FXML
private void playSong1()
{
playSong(songs[0], 0);
playBtn1.setText("Playing");
playBtn2.setText("Play");
playBtn3.setText("Play");
}
@FXML
private void playSong2()
{
playSong(songs[1], 1);
playBtn1.setText("Play");
playBtn2.setText("Playing");
playBtn3.setText("Play");
}
@FXML
private void playSong3()
{
playSong(songs[2], 2);
playBtn1.setText("Play");
playBtn2.setText("Play");
playBtn3.setText("Playing");
}
private void playSong(Song song, int index)
{
nowPlayingLabel.setText("Now Playing: " + song.getTitle() + " - " + song.getArtist());
setActiveRow(index);
}
private void setActiveRow(int index)
{
Label[] labels = {song1Title, song2Title, song3Title};
for (int i = 0; i < labels.length; i++)
{
if (i == index)
labels[i].setStyle("-fx-font-size: 22px; -fx-font-weight: bold; -fx-background-color: #dbeafe;");
else
labels[i].setStyle("-fx-font-size: 22px;");
}
}
// -----------------------------
// Playlist page
// -----------------------------
@FXML
private void openPlaylistPage()
{
try
{
FXMLLoader loader = new FXMLLoader(MusicApp.class.getResource("playlist-view.fxml"));
Scene scene = new Scene(loader.load(), 800, 600);
PlaylistController controller = loader.getController();
controller.setPlaylist(playlist);
Stage stage = (Stage) nowPlayingLabel.getScene().getWindow();
stage.setScene(scene);
stage.setTitle("Playlist");
}
catch (IOException e)
{
nowPlayingLabel.setText("Failed to open playlist page.");
e.printStackTrace();
}
}
// -----------------------------
// Theme toggle
// -----------------------------
@FXML
private void handleThemeToggle()
{
if (themeToggle.isSelected())
{
applyDarkTheme();
themeToggle.setText("Light Mode");
}
else
{
applyLightTheme();
themeToggle.setText("Dark Mode");
}
}
private void applyDarkTheme()
{
Scene scene = nowPlayingLabel.getScene();
if (scene == null)
return;
scene.getRoot().setStyle("-fx-background-color: #1f2937;" + "-fx-text-fill: #f9fafb;");
nowPlayingLabel.setStyle("-fx-font-size: 24px; -fx-text-fill: #f9fafb;");
song1Title.setStyle("-fx-font-size: 22px; -fx-text-fill: #f9fafb;");
song2Title.setStyle("-fx-font-size: 22px; -fx-text-fill: #f9fafb;");
song3Title.setStyle("-fx-font-size: 22px; -fx-text-fill: #f9fafb;");
song1Artist.setStyle("-fx-font-size: 22px; -fx-text-fill: #d1d5db;");
song2Artist.setStyle("-fx-font-size: 22px; -fx-text-fill: #d1d5db;");
song3Artist.setStyle("-fx-font-size: 22px; -fx-text-fill: #d1d5db;");
song1Duration.setStyle("-fx-font-size: 22px; -fx-text-fill: #d1d5db;");
song2Duration.setStyle("-fx-font-size: 22px; -fx-text-fill: #d1d5db;");
song3Duration.setStyle("-fx-font-size: 22px; -fx-text-fill: #d1d5db;");
themeToggle.setStyle(
"-fx-background-color: #374151;" +
"-fx-text-fill: white;" +
"-fx-border-color: #9ca3af;"
);
}
private void applyLightTheme()
{
Scene scene = nowPlayingLabel.getScene();
if (scene == null)
return;
scene.getRoot().setStyle("-fx-background-color: white;" + "-fx-text-fill: black;");
nowPlayingLabel.setStyle("-fx-font-size: 24px; -fx-text-fill: black;");
song1Title.setStyle("-fx-font-size: 22px; -fx-text-fill: black;");
song2Title.setStyle("-fx-font-size: 22px; -fx-text-fill: black;");
song3Title.setStyle("-fx-font-size: 22px; -fx-text-fill: black;");
song1Artist.setStyle("-fx-font-size: 22px; -fx-text-fill: #444444;");
song2Artist.setStyle("-fx-font-size: 22px; -fx-text-fill: #444444;");
song3Artist.setStyle("-fx-font-size: 22px; -fx-text-fill: #444444;");
song1Duration.setStyle("-fx-font-size: 22px; -fx-text-fill: #444444;");
song2Duration.setStyle("-fx-font-size: 22px; -fx-text-fill: #444444;");
song3Duration.setStyle("-fx-font-size: 22px; -fx-text-fill: #444444;");
themeToggle.setStyle(
"-fx-background-color: white;" +
"-fx-text-fill: black;" +
"-fx-border-color: black;"
);
} }
} }
@@ -0,0 +1,57 @@
package sbu.javafx;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.fxml.FXMLLoader;
import java.io.IOException;
import java.util.ArrayList;
public class PlaylistController
{
@FXML
private ListView<String> playlistListView;
private ArrayList<Song> playlist = new ArrayList<>();
public void setPlaylist(ArrayList<Song> playlist)
{
this.playlist = playlist;
refreshPlaylist();
}
private void refreshPlaylist()
{
playlistListView.getItems().clear();
if (playlist.isEmpty())
playlistListView.getItems().add("Playlist is empty.");
else
for (Song song : playlist)
playlistListView.getItems().add(song.toString());
}
@FXML
private void goBack()
{
try
{
FXMLLoader loader = new FXMLLoader(MusicApp.class.getResource("App-view.fxml"));
Scene scene = new Scene(loader.load(), 1280, 900);
Stage stage = (Stage) playlistListView.getScene().getWindow();
stage.setScene(scene);
stage.setTitle("Music Player");
}
catch (IOException e)
{
e.printStackTrace();
Alert alert = new Alert(Alert.AlertType.ERROR, "Failed to return to main screen.", ButtonType.OK);
alert.showAndWait();
}
}
}
+33
View File
@@ -0,0 +1,33 @@
package sbu.javafx;
public class Song
{
private final String title;
private final String artist;
private final String duration;
public Song(String title, String artist, String duration)
{
this.title = title;
this.artist = artist;
this.duration = duration;
}
public String getTitle() {
return title;
}
public String getArtist() {
return artist;
}
public String getDuration() {
return duration;
}
@Override
public String toString()
{
return title + " - " + artist + " (" + duration + ")";
}
}
+138 -4
View File
@@ -1,8 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?> <?import javafx.geometry.Insets?>
<VBox xmlns="http://javafx.com/fxml" alignment="CENTER" spacing="10"> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<!-- TODO: Add your code here! --> <BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="sbu.javafx.MusicController"
prefWidth="1280.0" prefHeight="900.0"
style="-fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;">
</VBox> <top>
<VBox spacing="20" alignment="CENTER" BorderPane.alignment="CENTER">
<padding>
<Insets top="20" right="20" bottom="10" left="20"/>
</padding>
<HBox alignment="CENTER_RIGHT" prefWidth="1200">
<ToggleButton fx:id="themeToggle"
text="Dark Mode"
onAction="#handleThemeToggle"
style="-fx-font-size: 14px; -fx-padding: 8 14 8 14;"/>
</HBox>
<Label text="Music Player"
style="-fx-font-size: 48px; -fx-font-weight: bold; -fx-text-fill: black;"/>
</VBox>
</top>
<center>
<VBox spacing="20" alignment="TOP_CENTER">
<padding>
<Insets top="10" right="20" bottom="10" left="20"/>
</padding>
<GridPane fx:id="songsGrid"
hgap="20" vgap="0"
style="-fx-border-color: black; -fx-border-width: 2;"
prefWidth="1200">
<columnConstraints>
<ColumnConstraints percentWidth="24"/>
<ColumnConstraints percentWidth="24"/>
<ColumnConstraints percentWidth="12"/>
<ColumnConstraints percentWidth="22"/>
<ColumnConstraints percentWidth="18"/>
</columnConstraints>
<!-- Header -->
<Label text="Song Title" GridPane.rowIndex="0" GridPane.columnIndex="0"
style="-fx-font-size: 24px; -fx-font-weight: bold;"/>
<Label text="Artist Name" GridPane.rowIndex="0" GridPane.columnIndex="1"
style="-fx-font-size: 24px; -fx-font-weight: bold;"/>
<Label text="Duration" GridPane.rowIndex="0" GridPane.columnIndex="2"
style="-fx-font-size: 24px; -fx-font-weight: bold;"/>
<Label text="Add to Playlist" GridPane.rowIndex="0" GridPane.columnIndex="3"
style="-fx-font-size: 24px; -fx-font-weight: bold;"/>
<Label text="Play" GridPane.rowIndex="0" GridPane.columnIndex="4"
style="-fx-font-size: 24px; -fx-font-weight: bold;"/>
<!-- Song 1 -->
<Separator GridPane.rowIndex="1" prefWidth="1200"/>
<Label fx:id="song1Title" text="Somewhere I Belong" GridPane.rowIndex="2" GridPane.columnIndex="0"
style="-fx-font-size: 22px;"/>
<Label fx:id="song1Artist" text="Linkin Park" GridPane.rowIndex="2" GridPane.columnIndex="1"
style="-fx-font-size: 22px;"/>
<Label fx:id="song1Duration" text="03:45" GridPane.rowIndex="2" GridPane.columnIndex="2"
style="-fx-font-size: 22px;"/>
<Button text="Add to Playlist" GridPane.rowIndex="2" GridPane.columnIndex="3"
onAction="#addSong1ToPlaylist"
style="-fx-font-size: 18px; -fx-padding: 12 20 12 20; -fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;"/>
<Button fx:id="playBtn1"
text="Play" GridPane.rowIndex="2" GridPane.columnIndex="4"
onAction="#playSong1"
style="-fx-font-size: 18px; -fx-padding: 12 25 12 25; -fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;"/>
<!-- Song 2 -->
<Separator GridPane.rowIndex="3" prefWidth="1200"/>
<Label fx:id="song2Title" text="Let Down" GridPane.rowIndex="4" GridPane.columnIndex="0"
style="-fx-font-size: 22px;"/>
<Label fx:id="song2Artist" text="Radiohead" GridPane.rowIndex="4" GridPane.columnIndex="1"
style="-fx-font-size: 22px;"/>
<Label fx:id="song2Duration" text="04:12" GridPane.rowIndex="4" GridPane.columnIndex="2"
style="-fx-font-size: 22px;"/>
<Button text="Add to Playlist" GridPane.rowIndex="4" GridPane.columnIndex="3"
onAction="#addSong2ToPlaylist"
style="-fx-font-size: 18px; -fx-padding: 12 20 12 20; -fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;"/>
<Button fx:id="playBtn2"
text="Play" GridPane.rowIndex="4" GridPane.columnIndex="4"
onAction="#playSong2"
style="-fx-font-size: 18px; -fx-padding: 12 25 12 25; -fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;"/>
<!-- Song 3 -->
<Separator GridPane.rowIndex="5" prefWidth="1200"/>
<Label fx:id="song3Title" text="Snuff" GridPane.rowIndex="6" GridPane.columnIndex="0"
style="-fx-font-size: 22px;"/>
<Label fx:id="song3Artist" text="Slipknot" GridPane.rowIndex="6" GridPane.columnIndex="1"
style="-fx-font-size: 22px;"/>
<Label fx:id="song3Duration" text="05:08" GridPane.rowIndex="6" GridPane.columnIndex="2"
style="-fx-font-size: 22px;"/>
<Button text="Add to Playlist" GridPane.rowIndex="6" GridPane.columnIndex="3"
onAction="#addSong3ToPlaylist"
style="-fx-font-size: 18px; -fx-padding: 12 20 12 20; -fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;"/>
<Button fx:id="playBtn3"
text="Play" GridPane.rowIndex="6" GridPane.columnIndex="4"
onAction="#playSong3"
style="-fx-font-size: 18px; -fx-padding: 12 25 12 25; -fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;"/>
</GridPane>
</VBox>
</center>
<bottom>
<VBox spacing="15" alignment="CENTER" BorderPane.alignment="CENTER">
<padding>
<Insets top="10" right="20" bottom="20" left="20"/>
</padding>
<GridPane style="-fx-border-color: black; -fx-border-width: 2;" prefWidth="1200">
<padding>
<Insets top="30" right="30" bottom="30" left="30"/>
</padding>
<columnConstraints>
<ColumnConstraints percentWidth="70"/>
<ColumnConstraints percentWidth="30"/>
</columnConstraints>
<Label fx:id="nowPlayingLabel"
text="Now Playing: -"
GridPane.columnIndex="0"
style="-fx-font-size: 24px;"/>
<Button text="Open Playlist"
GridPane.columnIndex="1"
onAction="#openPlaylistPage"
style="-fx-font-size: 20px; -fx-padding: 12 30 12 30; -fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;"/>
</GridPane>
</VBox>
</bottom>
</BorderPane>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="sbu.javafx.PlaylistController"
prefWidth="800.0" prefHeight="600.0"
style="-fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;">
<top>
<VBox alignment="CENTER" spacing="10">
<padding>
<Insets top="20" right="20" bottom="10" left="20"/>
</padding>
<Label text="Playlist" style="-fx-font-size: 42px; -fx-font-weight: bold;"/>
</VBox>
</top>
<center>
<VBox alignment="CENTER" spacing="15">
<padding>
<Insets top="20" right="30" bottom="20" left="30"/>
</padding>
<ListView fx:id="playlistListView" prefHeight="400" prefWidth="700"/>
</VBox>
</center>
<bottom>
<HBox alignment="CENTER_RIGHT">
<padding>
<Insets top="10" right="30" bottom="30" left="30"/>
</padding>
<Button text="Back"
onAction="#goBack"
style="-fx-font-size: 18px; -fx-padding: 10 24 10 24; -fx-background-color: white; -fx-border-color: black; -fx-border-width: 2;"/>
</HBox>
</bottom>
</BorderPane>