forked from AdvancedProgramming1404/HW-07-JavaFX
HW7
This commit is contained in:
Generated
+1
-1
@@ -8,7 +8,7 @@
|
||||
</list>
|
||||
</option>
|
||||
</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" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<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
|
||||
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<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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?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! -->
|
||||
|
||||
<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>
|
||||
|
||||
@@ -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; }
|
||||
@@ -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; }
|
||||
Reference in New Issue
Block a user