Skip to content

implimented a proper mail app and refactored desktop view controller #119

Merged
merged 1 commit into from
May 18, 2026
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public final class AudioManager {
private MediaPlayer bgPlayer;

private final DoubleProperty masterVolume = new SimpleDoubleProperty(0.8);
private final DoubleProperty sfxVolume = new SimpleDoubleProperty(0.8);
private final DoubleProperty sfxVolume = new SimpleDoubleProperty(0.8);

private AudioManager() {
for (Audio audio : Audio.values()) {
Expand Down Expand Up @@ -60,7 +60,7 @@ public void playSFX(final Audio audio) {

new Thread(() -> {
try {
var bais = new java.io.ByteArrayInputStream(data);
var bais = new java.io.ByteArrayInputStream(data);
var stream = AudioSystem.getAudioInputStream(bais);
Clip clip = AudioSystem.getClip();
clip.open(stream);
Expand Down Expand Up @@ -115,7 +115,7 @@ public void stopBgMusic() {
/**
* Sfxvolumeproperty method.
*/
public DoubleProperty sfxVolumeProperty() { return sfxVolume; }
public DoubleProperty sfxVolumeProperty() { return sfxVolume; }

/**
* Returns the mastervolume.
Expand All @@ -128,20 +128,20 @@ public void stopBgMusic() {
*
* @return the value
*/
public double getSfxVolume() { return sfxVolume.get(); }
public double getSfxVolume() { return sfxVolume.get(); }

/**
* Sets the mastervolume.
*
* @param value the new value
* @param v the new value
*/
public void setMasterVolume(final double v) { masterVolume.set(clamp(v)); }
/**
* Sets the sfxvolume.
*
* @param value the new value
* @param v the new value
*/
public void setSfxVolume(final double v) { sfxVolume.set(clamp(v)); }
public void setSfxVolume(final double v) { sfxVolume.set(clamp(v)); }


/** Converts a 0.0–1.0 linear volume to decibels for FloatControl. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers;

import edu.ntnu.idi.idatt2003.gruppe42.Model.Day;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Message;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.IntegerBinding;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

/**
* Controller for the Mail app, managing incoming messages and unread status.
*/
public class MailController implements AppController {
private final ObservableList<Message> messages = FXCollections.observableArrayList();
private final IntegerBinding unreadCount;

/**
* Constructs the MailController and sets up the unread count binding.
*/
public MailController() {
unreadCount = Bindings.createIntegerBinding(() ->
(int) messages.stream().filter(m -> !m.isSeen()).count(),
messages
);

// Ensure unreadCount updates when a message's seen property changes
messages.addListener((ListChangeListener<Message>) c -> {
while (c.next()) {
if (c.wasAdded()) {
for (Message m : c.getAddedSubList()) {
m.seenProperty().addListener((obs, oldVal, newVal) -> unreadCount.invalidate());
}
}
}
unreadCount.invalidate();
});
}

/**
* @return the list of messages.
*/
public ObservableList<Message> getMessages() {
return messages;
}

/**
* @return a binding to the number of unread messages.
*/
public IntegerBinding unreadCountProperty() {
return unreadCount;
}

/**
* Creates and adds a new message to the inbox.
*
* @param author the sender
* @param title the subject
* @param message the content
* @param week the week
* @param day the day
* @param time the time
*/
public void createMessage(String author, String title, String message, int week, Day day, int time) {
messages.add(new Message(author, title, message, week, day, time));
}

@Override
public void nextTick() {
// No specific tick logic currently needed for mail
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public final class GameController {
private final List<AppController> appControllers = new ArrayList<>();
private Timer timer;
private GameState gameState;
private final AudioManager audioManager = AudioManager.get(); // ← was getInstance()
private boolean workMusicPlaying = false;
private final AudioManager audioManager = AudioManager.get();
private boolean workMusicPlaying = false;
private boolean weekendMusicPlaying = false;

/** Adds an app controller. */
Expand All @@ -30,21 +30,21 @@ public void setGameState(final GameState gameState) {

if (gameState == GameState.WORKDAY && !workMusicPlaying) {
audioManager.playBgMusic(Audio.WORK_THEME);
workMusicPlaying = true;
workMusicPlaying = true;
weekendMusicPlaying = false;
}

if (gameState == GameState.FREEDAY && !weekendMusicPlaying) {
audioManager.playBgMusic(Audio.WEEKEND_THEME);
weekendMusicPlaying = true;
workMusicPlaying = false;
workMusicPlaying = false;
}
}

/** Starts the game loop. */
public void startGame() {
timer = new Timer(true);
final int delay = 0;
final int delay = 0;
final int period = 1000;

timer.scheduleAtFixedRate(new TimerTask() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package edu.ntnu.idi.idatt2003.gruppe42.Controller.ViewControllers;

import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.MailController;
import edu.ntnu.idi.idatt2003.gruppe42.Controller.PopupsController;
import edu.ntnu.idi.idatt2003.gruppe42.Model.App;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;

/** Factory for creating desktop UI components. */
public final class DesktopComponentFactory {

private final PopupsController popupsController;
private final MailController mailController;

/**
* Constructs the factory.
*
* @param popupsController the popups controller
* @param mailController the mail controller
*/
public DesktopComponentFactory(PopupsController popupsController, MailController mailController) {
this.popupsController = popupsController;
this.mailController = mailController;
}

/**
* Creates an app button.
*
* @param type the app type
* @return the node representing the app button
*/
public Node createAppButton(final App type) {
Button button = new Button(type.getDisplayName());
button.getStyleClass().add("app-button");
button.setMinSize(0, 0);

button.parentProperty().addListener((observable, oldParent, newParent) -> {
if (newParent instanceof Region region) {
button.prefWidthProperty().bind(
Bindings.min(
region.widthProperty().multiply(0.8),
region.heightProperty()
).multiply(0.8)
);
button.prefHeightProperty().bind(button.prefWidthProperty());
} else {
button.prefWidthProperty().unbind();
button.prefHeightProperty().unbind();
}
});

popupsController.bindOpenButton(button, type);

button.setOnDragDetected(event -> {
Dragboard dragboard = button.startDragAndDrop(TransferMode.MOVE);
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
dragboard.setDragView(button.snapshot(params, null));
dragboard.setDragViewOffsetX(event.getX());
dragboard.setDragViewOffsetY(event.getY());
ClipboardContent content = new ClipboardContent();
content.putString("app_button");
dragboard.setContent(content);
event.consume();
});

if (type == App.MAIL) {
StackPane container = new StackPane();

Circle badge = new Circle(10, Color.RED);
Label countLabel = new Label();
countLabel.setTextFill(Color.WHITE);
countLabel.setFont(Font.font("System", FontWeight.BOLD, 10));
countLabel.textProperty().bind(mailController.unreadCountProperty().asString());

StackPane badgeUI = new StackPane(badge, countLabel);
badgeUI.setMouseTransparent(true);
badgeUI.setAlignment(Pos.CENTER);
badgeUI.visibleProperty().bind(mailController.unreadCountProperty().greaterThan(0));

container.getChildren().addAll(button, badgeUI);
StackPane.setAlignment(badgeUI, Pos.TOP_RIGHT);
StackPane.setMargin(badgeUI, new Insets(-5, -5, 0, 0));

return container;
}

return button;
}

/**
* Configures a stack pane as a drop target for app buttons.
*
* @param cell the stack pane to configure
*/
public void configureCellAsDropTarget(final StackPane cell) {
cell.setOnDragOver(event -> {
if (event.getGestureSource() instanceof Button && cell.getChildren().isEmpty()) {
event.acceptTransferModes(TransferMode.MOVE);
}
event.consume();
});

cell.setOnDragDropped(event -> {
boolean success = false;
if (event.getGestureSource() instanceof Button button) {
((StackPane) button.getParent()).getChildren().remove(button);
cell.getChildren().add(button);
success = true;
}
event.setDropCompleted(success);
event.consume();
});
}
}
Loading