HW7 #1

Open
Arefe00 wants to merge 1 commits from develop into main
8 changed files with 462 additions and 9 deletions
+1 -1
View File
@@ -8,7 +8,7 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="23" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_25_PREVIEW" project-jdk-name="23" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+3 -1
View File
@@ -4,5 +4,7 @@ import javafx.application.Application;
public class Launcher{ public class Launcher{
public static void main(String[] args) { 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);
}
} }
+1
View File
@@ -14,6 +14,7 @@ public class MusicApp extends Application {
{ {
FXMLLoader fxmlLoader = new FXMLLoader(MusicApp.class.getResource("App-view.fxml")); FXMLLoader fxmlLoader = new FXMLLoader(MusicApp.class.getResource("App-view.fxml"));
Scene scene = new Scene(fxmlLoader.load()); Scene scene = new Scene(fxmlLoader.load());
scene.getStylesheets().add(MusicApp.class.getResource("light.css").toExternalForm());
stage.setTitle("Music Player"); stage.setTitle("Music Player");
stage.setScene(scene); stage.setScene(scene);
//TODO: add icon to the stage //TODO: add icon to the stage
+126 -6
View File
@@ -2,30 +2,150 @@ package sbu.javafx;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.event.ActionEvent; 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.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 { public class MusicController {
// TODO: Define your UI components using the @FXML annotation. // 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<String> 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 @FXML
public void handlePlayAction() { 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. //TODO: Logic to add a specific song to the user's playlist.
public void addToPlaylist(String songName) { public void addToPlaylist(String songName) {
//Add the song to a list or update a ListView. //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. // TODO:This method should open a new window or change the current scene to display the "Playlist" page.
@FXML
public void openPlaylistWindow() { public void openPlaylistWindow() {
//Create a new stage and show the playlist UI. //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<String> 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");
}
} }
} }
+39 -1
View File
@@ -1,8 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/fxml" alignment="CENTER" spacing="10"> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.Priority?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.geometry.Insets?>
<VBox xmlns="http://javafx.com/fxml"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sbu.javafx.MusicController"
alignment="CENTER" spacing="10"
prefWidth="640" prefHeight="520">
<!-- TODO: Add your code here! --> <!-- TODO: Add your code here! -->
<padding><Insets top="0" right="0" bottom="0" left="0"/></padding>
<HBox alignment="CENTER_LEFT" spacing="12" styleClass="header">
<padding><Insets top="16" right="20" bottom="16" left="20"/></padding>
<Label text="🎵 Music Player" styleClass="app-title"/>
<Region HBox.hgrow="ALWAYS"/>
<Button fx:id="themeToggleBtn" text="🌙 Dark Mode" onAction="#handleThemeToggle" styleClass="theme-btn"/>
</HBox>
<Label fx:id="nowPlayingLabel" text="Now Playing: —"
maxWidth="Infinity" styleClass="now-playing">
<VBox.margin><Insets top="10" right="20" bottom="0" left="20"/></VBox.margin>
</Label>
<ScrollPane fitToWidth="true" styleClass="song-scroll" VBox.vgrow="ALWAYS">
<VBox.margin><Insets top="0" right="20" bottom="0" left="20"/></VBox.margin>
<VBox fx:id="songListVBox" spacing="10">
<padding><Insets top="4" bottom="4"/></padding>
</VBox>
</ScrollPane>
<Button text="📋 View Playlist" onAction="#openPlaylistWindow"
maxWidth="Infinity" styleClass="playlist-btn">
<VBox.margin><Insets top="0" right="20" bottom="16" left="20"/></VBox.margin>
</Button>
</VBox> </VBox>
+142
View File
@@ -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; }
+144
View File
@@ -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; }