Skip to content

Commit

Permalink
Merge pull request #119 from einaskoi/per/mail
Browse files Browse the repository at this point in the history
implimented a proper mail app and refactored desktop view controller
  • Loading branch information
einaskoi authored May 18, 2026
2 parents 572dbab + 50c8f17 commit 29a0aac
Show file tree
Hide file tree
Showing 13 changed files with 765 additions and 340 deletions.
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

0 comments on commit 29a0aac

Please sign in to comment.