diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/AudioManager.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/AudioManager.java index a9c3b53..bd02f21 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/AudioManager.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/AudioManager.java @@ -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()) { @@ -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); @@ -115,7 +115,7 @@ public void stopBgMusic() { /** * Sfxvolumeproperty method. */ - public DoubleProperty sfxVolumeProperty() { return sfxVolume; } + public DoubleProperty sfxVolumeProperty() { return sfxVolume; } /** * Returns the mastervolume. @@ -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. */ diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/MailController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/MailController.java new file mode 100644 index 0000000..184b32b --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/MailController.java @@ -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 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) 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 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 + } +} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/GameController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/GameController.java index 2edd7e0..3907365 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/GameController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/GameController.java @@ -15,8 +15,8 @@ public final class GameController { private final List 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. */ @@ -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() { diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopComponentFactory.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopComponentFactory.java new file mode 100644 index 0000000..1ce0450 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopComponentFactory.java @@ -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(); + }); + } +} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopDialogController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopDialogController.java new file mode 100644 index 0000000..6c5d9db --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopDialogController.java @@ -0,0 +1,157 @@ +package edu.ntnu.idi.idatt2003.gruppe42.Controller.ViewControllers; + +import edu.ntnu.idi.idatt2003.gruppe42.Controller.TimeAndWeatherController; +import edu.ntnu.idi.idatt2003.gruppe42.Model.Player; +import edu.ntnu.idi.idatt2003.gruppe42.Model.Transaction.Transaction; +import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.GameOverApp; +import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.WarningApp; +import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.WeekendRapportApp; +import edu.ntnu.idi.idatt2003.gruppe42.View.Views.DesktopView; +import edu.ntnu.idi.idatt2003.gruppe42.View.Views.DesktopView.ModalLayer; +import java.math.BigDecimal; +import java.util.List; + +/** Controller for handling desktop dialogs and modals. */ +public final class DesktopDialogController { + + private static final BigDecimal WEEKLY_RENT = new BigDecimal("50.00"); + + private final Player player; + private final TimeAndWeatherController timeAndWeatherController; + private final WeekendRapportApp weekendRapportApp; + private final WarningApp warningApp; + private final GameOverApp gameOverApp; + private DesktopView desktopView; + private Runnable onGameOver; + private Runnable onAdvanceWeek; + + /** + * Constructs the controller. + * + * @param player the player model + * @param timeAndWeatherController the time and weather controller + * @param weekendRapportApp the weekend rapport app + * @param warningApp the warning app + * @param gameOverApp the game over app + */ + public DesktopDialogController( + Player player, + TimeAndWeatherController timeAndWeatherController, + WeekendRapportApp weekendRapportApp, + WarningApp warningApp, + GameOverApp gameOverApp) { + this.player = player; + this.timeAndWeatherController = timeAndWeatherController; + this.weekendRapportApp = weekendRapportApp; + this.warningApp = warningApp; + this.gameOverApp = gameOverApp; + + wireWeekendRapport(); + wireGameOver(); + } + + public void setDesktopView(DesktopView desktopView) { + this.desktopView = desktopView; + } + + public void setOnGameOver(Runnable onGameOver) { + this.onGameOver = onGameOver; + } + + public void setOnAdvanceWeek(Runnable onAdvanceWeek) { + this.onAdvanceWeek = onAdvanceWeek; + } + + /** Shows the weekend rapport. */ + public void showWeekendRapport() { + int week = timeAndWeatherController.weekIndexProperty().get(); + BigDecimal netIncome = player.getTransactionArchive().calculateNetIncome(week); + List transactions = player.getTransactionArchive().getTransactions(week); + BigDecimal cashBefore = player.getMoney(); + + player.deductRent(WEEKLY_RENT); + BigDecimal cashAfter = player.getMoney(); + + weekendRapportApp.populate(transactions, netIncome, cashBefore, WEEKLY_RENT, cashAfter); + weekendRapportApp.show(); + desktopView.enterLayer(ModalLayer.RAPPORT); + } + + private void wireWeekendRapport() { + weekendRapportApp.getContinueButton().setOnAction(event -> { + weekendRapportApp.getRoot().setVisible(false); + desktopView.exitLayer(ModalLayer.RAPPORT); + timeAndWeatherController.setDay(0); + }); + } + + /** Shows the debt warning. */ + public void showDebtWarning() { + warningApp.configure( + "💔", + "Starting Week in Debt", + "Your balance is negative. Proceeding to next week will cost you a life.", + "Stay on Sunday", + "Proceed Anyway" + ); + warningApp.getSecondaryButton().setOnAction(event -> dismissWarning()); + warningApp.getPrimaryButton().setOnAction(event -> { + dismissWarning(); + player.loseLife(); + if (player.getLives() == 0) { + showGameOver(); + } else if (onAdvanceWeek != null) { + onAdvanceWeek.run(); + } + }); + showWarning(); + } + + /** Shows the market closed warning. */ + public void showMarketClosedWarning() { + warningApp.configure( + "🔒", + "Market Closed", + "Trading is suspended on weekends.\nThe market reopens on Monday.", + "Got it" + ); + warningApp.getPrimaryButton().setOnAction(event -> dismissWarning()); + showWarning(); + } + + /** Shows the insufficient funds warning. */ + public void showInsufficientFundsWarning() { + warningApp.configure( + "\uD83E\uDD7A", + "Insufficient Funds", + "You don't have enough cash to complete this purchase.", + "I'll be back" + ); + warningApp.getPrimaryButton().setOnAction(event -> dismissWarning()); + showWarning(); + } + + /** Shows a warning. */ + public void showWarning() { + warningApp.show(); + desktopView.enterLayer(ModalLayer.WARNING); + } + + /** Dismisses a warning. */ + public void dismissWarning() { + warningApp.getRoot().setVisible(false); + desktopView.exitLayer(ModalLayer.WARNING); + } + + /** Shows the game over screen. */ + public void showGameOver() { + gameOverApp.show(); + desktopView.enterLayer(ModalLayer.GAME_OVER); + } + + private void wireGameOver() { + gameOverApp.getStartOverButton().setOnAction(event -> { + if (onGameOver != null) onGameOver.run(); + }); + } +} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java index 621fe43..353051b 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java @@ -2,6 +2,7 @@ import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.AppStoreController; import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.BankAppController; +import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.MailController; import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.SettingsAppController; import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.StockAppController; import edu.ntnu.idi.idatt2003.gruppe42.Controller.GameController; @@ -10,9 +11,6 @@ import edu.ntnu.idi.idatt2003.gruppe42.Controller.TimeAndWeatherController; import edu.ntnu.idi.idatt2003.gruppe42.Model.App; import edu.ntnu.idi.idatt2003.gruppe42.Model.Player; -import edu.ntnu.idi.idatt2003.gruppe42.Model.Transaction.Purchase; -import edu.ntnu.idi.idatt2003.gruppe42.Model.Transaction.Sale; -import edu.ntnu.idi.idatt2003.gruppe42.Model.Transaction.Transaction; import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.AppStoreApp; import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.BankApp; import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.FilePickerApp; @@ -32,21 +30,13 @@ import java.util.ArrayList; import java.util.List; import javafx.application.Platform; -import javafx.beans.binding.Bindings; -import javafx.scene.SnapshotParameters; -import javafx.scene.control.Button; -import javafx.scene.input.ClipboardContent; -import javafx.scene.input.Dragboard; -import javafx.scene.input.TransferMode; +import javafx.scene.Node; import javafx.scene.layout.Pane; -import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; -import javafx.scene.paint.Color; /** Controller for the desktop view. */ public final class DesktopViewController { - private static final BigDecimal WEEKLY_RENT = new BigDecimal("50.00"); private static final int SATURDAY_INDEX = 6; private static final int SUNDAY_INDEX = 0; @@ -54,14 +44,16 @@ public final class DesktopViewController { private final Player player; private final TimeAndWeatherController timeAndWeatherController; private final MarketController marketController; - private final StockAppController stockAppController; - private final SettingsAppController settingsAppController; + private StockAppController stockAppController; + private SettingsAppController settingsAppController; + private final MailController mailController; + private final DesktopDialogController dialogController; + private final DesktopComponentFactory componentFactory; private final WeekendRapportApp weekendRapportApp = new WeekendRapportApp(); private final WarningApp warningApp = new WarningApp(); private final GameOverApp gameOverApp = new GameOverApp(); private DesktopView desktopView = null; - private Runnable onGameOver; /** Constructs the controller. */ public DesktopViewController( @@ -75,7 +67,31 @@ public DesktopViewController( gameController.addAppController(timeAndWeatherController); this.marketController = new MarketController(userSelectedPath); + this.mailController = new MailController(); + gameController.addAppController(mailController); + this.popupsController = popupsController; + this.componentFactory = new DesktopComponentFactory(popupsController, mailController); + this.dialogController = new DesktopDialogController( + player, timeAndWeatherController, weekendRapportApp, warningApp, gameOverApp); + + this.desktopView = new DesktopView(this); + this.dialogController.setDesktopView(desktopView); + this.dialogController.setOnAdvanceWeek(this::doAdvanceWeek); + + List popups = initializeApps(gameController, userSelectedPath); + this.popupsController.addPopups(popups); + + setupPopupOverlay(popups); + setupDesktopUI(); + + setupListeners(); + + int startDay = timeAndWeatherController.dayIndexProperty().get(); + stockAppController.setWeekend(startDay == SATURDAY_INDEX || startDay == SUNDAY_INDEX); + } + + private List initializeApps(GameController gameController, Path userSelectedPath) { AppStoreApp appStoreApp = new AppStoreApp(); gameController.addAppController(new AppStoreController(appStoreApp)); @@ -85,7 +101,7 @@ public DesktopViewController( this.stockAppController = new StockAppController(stockApp, marketController, player); gameController.addAppController(stockAppController); - MailApp mailApp = new MailApp(); + MailApp mailApp = new MailApp(mailController); NewsApp newsApp = new NewsApp(); BankApp bankApp = new BankApp(); @@ -104,17 +120,6 @@ public DesktopViewController( settingsAppController.setOnLogout(this::handleLogout); settingsAppController.setOnPowerOff(Platform::exit); - List popups = new ArrayList<>(); - popups.add(appStoreApp); - popups.add(hustlersApp); - popups.add(stockApp); - popups.add(mailApp); - popups.add(newsApp); - popups.add(bankApp); - - this.popupsController = popupsController; - this.popupsController.addPopups(popups); - bankApp.setOnShareSelected(share -> { popupsController.show(App.STOCK); stockApp.showStockPage(share.getStock()); @@ -123,10 +128,18 @@ public DesktopViewController( stockAppController.setOnMarketClosed(this::showMarketClosedWarning); stockAppController.setOnInsufficientFunds(this::showInsufficientFundsWarning); - this.desktopView = new DesktopView(this); - setupListeners(); + List popups = new ArrayList<>(); + popups.add(appStoreApp); + popups.add(hustlersApp); + popups.add(stockApp); + popups.add(mailApp); + popups.add(newsApp); + popups.add(bankApp); + + return popups; + } - Pane root = desktopView.getRoot(); + private void setupPopupOverlay(List popups) { Pane overlay = desktopView.getPopupOverlay(); for (Popup p : popupsController.getPopups()) { @@ -150,10 +163,9 @@ public DesktopViewController( desktopView.getOverlay(ModalLayer.RAPPORT).setOnMouseClicked( event -> weekendRapportApp.getContinueButton().fire()); + } - wireWeekendRapport(); - wireGameOver(); - + private void setupDesktopUI() { desktopView.getNextWeekButton().setOnAction(event -> handleAdvanceWeek()); desktopView.getSettingsButton().setOnAction(event -> popupsController.show(App.SETTINGS)); @@ -165,9 +177,6 @@ public DesktopViewController( Platform.runLater(this::showWeekendRapport); } }); - - int startDay = timeAndWeatherController.dayIndexProperty().get(); - stockAppController.setWeekend(startDay == SATURDAY_INDEX || startDay == SUNDAY_INDEX); } private void setupListeners() { @@ -201,30 +210,12 @@ private void setupListeners() { } private void showWeekendRapport() { - int week = timeAndWeatherController.weekIndexProperty().get(); - List transactions = player.getTransactionArchive().getTransactions(week); - BigDecimal netIncome = calculateNetIncome(transactions); - BigDecimal cashBefore = player.getMoney(); - - player.deductRent(WEEKLY_RENT); - BigDecimal cashAfter = player.getMoney(); - - weekendRapportApp.populate(transactions, netIncome, cashBefore, WEEKLY_RENT, cashAfter); - weekendRapportApp.show(); - desktopView.enterLayer(ModalLayer.RAPPORT); - } - - private void wireWeekendRapport() { - weekendRapportApp.getContinueButton().setOnAction(event -> { - weekendRapportApp.getRoot().setVisible(false); - desktopView.exitLayer(ModalLayer.RAPPORT); - timeAndWeatherController.setDay(0); - }); + dialogController.showWeekendRapport(); } private void handleAdvanceWeek() { if (player.isInDebt()) { - showDebtWarning(); + dialogController.showDebtWarning(); } else { doAdvanceWeek(); } @@ -235,137 +226,35 @@ private void doAdvanceWeek() { timeAndWeatherController.advanceWeek(); } - private void showDebtWarning() { - warningApp.configure( - "💔", - "Starting Week in Debt", - "Your balance is negative. Proceeding to next week will cost you a life.", - "Stay on Sunday", - "Proceed Anyway" - ); - warningApp.getSecondaryButton().setOnAction(event -> dismissWarning()); - warningApp.getPrimaryButton().setOnAction(event -> { - dismissWarning(); - player.loseLife(); - if (player.getLives() == 0) { - showGameOver(); - } else { - doAdvanceWeek(); - } - }); - showWarning(); - } - private void showMarketClosedWarning() { - warningApp.configure( - "🔒", - "Market Closed", - "Trading is suspended on weekends.\nThe market reopens on Monday.", - "Got it" - ); - warningApp.getPrimaryButton().setOnAction(event -> dismissWarning()); - showWarning(); + dialogController.showMarketClosedWarning(); } private void showInsufficientFundsWarning() { - warningApp.configure( - "\uD83E\uDD7A", - "Insufficient Funds", - "You don't have enough cash to complete this purchase.", - "I'll be back" - ); - warningApp.getPrimaryButton().setOnAction(event -> dismissWarning()); - showWarning(); - } - - private void showWarning() { - warningApp.show(); - desktopView.enterLayer(ModalLayer.WARNING); - } - - private void dismissWarning() { - warningApp.getRoot().setVisible(false); - desktopView.exitLayer(ModalLayer.WARNING); + dialogController.showInsufficientFundsWarning(); } private void showGameOver() { - gameOverApp.show(); - desktopView.enterLayer(ModalLayer.GAME_OVER); - } - - private void wireGameOver() { - gameOverApp.getStartOverButton().setOnAction(event -> { - if (onGameOver != null) onGameOver.run(); - }); + dialogController.showGameOver(); } /** Sets the game over callback. */ public void setOnGameOver(final Runnable callback) { - this.onGameOver = callback; + dialogController.setOnGameOver(callback); } private void handleLogout() { - if (onGameOver != null) onGameOver.run(); + dialogController.showGameOver(); // Or appropriate logout logic } /** Creates an app button. */ - public Button 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(); - }); - - return button; + public Node createAppButton(final App type) { + return componentFactory.createAppButton(type); } /** Configures StackPane as drop target. */ 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(); - }); + componentFactory.configureCellAsDropTarget(cell); } /** @return the player model. */ @@ -383,15 +272,4 @@ public DesktopView getDesktopView() { return desktopView; } - private static BigDecimal calculateNetIncome(final List transactions) { - BigDecimal net = BigDecimal.ZERO; - for (Transaction t : transactions) { - if (t instanceof Sale) { - net = net.add(t.getCalculator().calculateTotal()); - } else if (t instanceof Purchase) { - net = net.subtract(t.getCalculator().calculateTotal()); - } - } - return net; - } } \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Message.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Message.java new file mode 100644 index 0000000..255960d --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Message.java @@ -0,0 +1,81 @@ +package edu.ntnu.idi.idatt2003.gruppe42.Model; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; + +/** + * Represents a mail message in the game. + */ +public class Message { + private final String author; + private final String title; + private final String content; + private final int week; + private final Day day; + private final int time; + private final BooleanProperty seen = new SimpleBooleanProperty(false); + + /** + * Constructs a new Message. + * + * @param author the sender of the message + * @param title the subject of the message + * @param content the body of the message + * @param week the week the message was sent + * @param day the day the message was sent + * @param time the time (hour) the message was sent + */ + public Message(String author, String title, String content, int week, Day day, int time) { + this.author = author; + this.title = title; + this.content = content; + this.week = week; + this.day = day; + this.time = time; + } + + public String getAuthor() { + return author; + } + + public String getTitle() { + return title; + } + + public String getContent() { + return content; + } + + public int getWeek() { + return week; + } + + public Day getDay() { + return day; + } + + public int getTime() { + return time; + } + + public boolean isSeen() { + return seen.get(); + } + + public void setSeen(boolean seen) { + this.seen.set(seen); + } + + public BooleanProperty seenProperty() { + return seen; + } + + /** + * @return the formatted time string, e.g., "Week 1, Mon, 13:00". + */ + public String getFormattedTime() { + String dayStr = day.toString().toLowerCase(); + dayStr = dayStr.substring(0, 1).toUpperCase() + dayStr.substring(1); + return String.format("Week %d, %s, %02d:00", week, dayStr, time); + } +} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Player.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Player.java index 668dd62..3ff6577 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Player.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Player.java @@ -13,7 +13,7 @@ public class Player { private static final int MAX_LIVES = 3; - private final StringProperty name = new SimpleStringProperty(); // ← was plain String + private final StringProperty name = new SimpleStringProperty(); private final BigDecimal startingMoney; private BigDecimal money; private final Portfolio portfolio; @@ -111,15 +111,15 @@ public TransactionArchive getTransactionArchive() { /** @return player status. */ public Status getStatus() { - final int investorWeeks = 10; - final int speculatorWeeks = 20; - final double investorMult = 1.2; - final double speculatorMult = 2.0; + final int investorWeeks = 10; + final int speculatorWeeks = 20; + final double investorMult = 1.2; + final double speculatorMult = 2.0; - int weeksActive = transactionArchive.countDistinctWeeks(); - BigDecimal netWorth = getNetWorth(); + int weeksActive = transactionArchive.countDistinctWeeks(); + BigDecimal netWorth = getNetWorth(); - BigDecimal investorTarget = startingMoney.multiply(BigDecimal.valueOf(investorMult)); + BigDecimal investorTarget = startingMoney.multiply(BigDecimal.valueOf(investorMult)); BigDecimal speculatorTarget = startingMoney.multiply(BigDecimal.valueOf(speculatorMult)); boolean isSpeculator = weeksActive >= speculatorWeeks diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Transaction/TransactionArchive.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Transaction/TransactionArchive.java index be40af8..b594724 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Transaction/TransactionArchive.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Transaction/TransactionArchive.java @@ -1,5 +1,6 @@ package edu.ntnu.idi.idatt2003.gruppe42.Model.Transaction; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -78,4 +79,22 @@ public int countDistinctWeeks() { return distinctWeeks.size(); } + + /** + * Calculates net income for a given week. + * + * @param week the week to calculate for + * @return net income + */ + public BigDecimal calculateNetIncome(final int week) { + BigDecimal net = BigDecimal.ZERO; + for (Transaction t : getTransactions(week)) { + if (t instanceof Sale) { + net = net.add(t.getCalculator().calculateTotal()); + } else if (t instanceof Purchase) { + net = net.subtract(t.getCalculator().calculateTotal()); + } + } + return net; + } } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/MailApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/MailApp.java index b11ed7a..fbe9267 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/MailApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/MailApp.java @@ -1,24 +1,41 @@ package edu.ntnu.idi.idatt2003.gruppe42.View.Apps; +import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.MailController; import edu.ntnu.idi.idatt2003.gruppe42.Model.App; +import edu.ntnu.idi.idatt2003.gruppe42.Model.Message; import edu.ntnu.idi.idatt2003.gruppe42.View.Popup; +import javafx.collections.ListChangeListener; import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.layout.VBox; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; -import javafx.geometry.Pos; -import javafx.scene.shape.Circle; -/** Mail app popup. */ +/** + * Mail app popup for viewing and managing messages. + */ public class MailApp extends Popup { + private final MailController controller; + private final VBox mailList; - /** Constructs the Mail app. */ - public MailApp() { + /** + * Constructs the Mail app. + * + * @param controller the controller managing mail data + */ + public MailApp(MailController controller) { super(450, 400, 140, 140, App.MAIL); + this.controller = controller; + this.mailList = new VBox(10); buildContent(); + setupListeners(); + refreshMailList(); } private void buildContent() { @@ -28,46 +45,63 @@ private void buildContent() { Label title = new Label("Inbox"); title.setFont(Font.font("System", FontWeight.BOLD, 22)); content.getChildren().add(title); - - VBox mailList = new VBox(10); - mailList.getChildren().addAll( - createMailItem("System", "Welcome to the Matrix", "Your journey begins now. Stay focused.", "10:24 AM"), - createMailItem("HR", "Onboarding Documents", "Please sign the attached NDA.", "Yesterday"), - createMailItem("Bank", "Account Security Alert", "A new login was detected from Dubai.", "2 days ago") - ); content.getChildren().add(mailList); } - private VBox createMailItem(String sender, String subject, String preview, String time) { + private void setupListeners() { + controller.getMessages().addListener((ListChangeListener) c -> refreshMailList()); + } + + private void refreshMailList() { + mailList.getChildren().clear(); + for (Message message : controller.getMessages()) { + mailList.getChildren().add(createMailItem(message)); + } + } + + private VBox createMailItem(Message message) { VBox item = new VBox(5); item.setPadding(new Insets(12)); - item.setStyle("-fx-background-color: #ffffff; -fx-border-color: #eeeeee; -fx-border-radius: 8; -fx-background-radius: 8;"); + item.setStyle("-fx-background-color: #ffffff; -fx-border-color: #eeeeee; " + + "-fx-border-radius: 8; -fx-background-radius: 8;"); HBox top = new HBox(10); top.setAlignment(Pos.CENTER_LEFT); - - Circle avatar = new Circle(15, Color.web("#007AFF")); - Label senderLabel = new Label(sender); + + Label senderLabel = new Label(message.getAuthor()); senderLabel.setFont(Font.font("System", FontWeight.BOLD, 14)); - - javafx.scene.layout.Region spacer = new javafx.scene.layout.Region(); - HBox.setHgrow(spacer, javafx.scene.layout.Priority.ALWAYS); - - Label timeLabel = new Label(time); + + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + + Label timeLabel = new Label(message.getFormattedTime()); timeLabel.setTextFill(Color.GRAY); timeLabel.setFont(Font.font(12)); - top.getChildren().addAll(avatar, senderLabel, spacer, timeLabel); + top.getChildren().addAll(senderLabel, spacer, timeLabel); - Label subLabel = new Label(subject); + Label subLabel = new Label(message.getTitle()); subLabel.setFont(Font.font("System", FontWeight.MEDIUM, 13)); - - Label prevLabel = new Label(preview); - prevLabel.setTextFill(Color.GRAY); - prevLabel.setWrapText(true); - item.getChildren().addAll(top, subLabel, prevLabel); + Label contentLabel = new Label(message.getContent()); + contentLabel.setTextFill(Color.GRAY); + contentLabel.setWrapText(true); + + item.getChildren().addAll(top, subLabel, contentLabel); + + if (!message.isSeen()) { + HBox bottom = new HBox(); + bottom.setAlignment(Pos.CENTER_RIGHT); + Button seenButton = new Button("Seen"); + seenButton.getStyleClass().add("primary-button"); + seenButton.setOnAction(event -> { + message.setSeen(true); + refreshMailList(); + }); + bottom.getChildren().add(seenButton); + item.getChildren().add(bottom); + } + return item; } - } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Popup.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Popup.java index b842f8a..cd7d645 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Popup.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Popup.java @@ -51,6 +51,7 @@ protected Popup(int width, int height, int x, int y, App type) { scrollPane.setMinSize(width, height); scrollPane.setFitToWidth(true); scrollPane.setFitToHeight(true); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); closeButton = buildCloseButton(); header = buildHeader(closeButton, type.getDisplayName()); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/Components/DesktopBottomBar.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/Components/DesktopBottomBar.java new file mode 100644 index 0000000..af35d30 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/Components/DesktopBottomBar.java @@ -0,0 +1,138 @@ +package edu.ntnu.idi.idatt2003.gruppe42.View.Views.Components; + +import edu.ntnu.idi.idatt2003.gruppe42.Model.Player; +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; + +/** + * The bottom bar of the desktop view. + * Displays player information, lives, game time, and status. + */ +public final class DesktopBottomBar extends HBox { + + private static final int HEART_COUNT = 3; + + private final Label playerLabel; + private final Label weatherLabel; + private final Label tempLabel; + private final Label dayLabel; + private final Label weekLabel; + private final Label clockLabel; + private final Button nextWeekButton; + private final Button settingsButton; + + /** + * Constructs the bottom bar. + * + * @param player the player model to display info from + */ + public DesktopBottomBar(Player player) { + super(15); + setAlignment(Pos.CENTER_LEFT); + setPadding(new Insets(5, 15, 5, 15)); + getStyleClass().add("desktop-bottom-bar"); + + settingsButton = new Button("⚙"); + settingsButton.getStyleClass().add("desktop-settings-button"); + + playerLabel = new Label("User: " + player.getName()); + playerLabel.getStyleClass().add("desktop-label-bold"); + playerLabel.setMinWidth(Region.USE_PREF_SIZE); + + HBox heartsBox = buildHeartsBox(player); + + nextWeekButton = new Button("Advance to next week"); + nextWeekButton.getStyleClass().add("primary-button"); + + Region spacerLeft = new Region(); + Region spacerRight = new Region(); + HBox.setHgrow(spacerLeft, Priority.ALWAYS); + HBox.setHgrow(spacerRight, Priority.ALWAYS); + + weatherLabel = styledLabel("desktop-label"); + tempLabel = styledLabel("desktop-label"); + dayLabel = styledLabel("desktop-label-bold"); + weekLabel = styledLabel("desktop-label-bold"); + clockLabel = styledLabel("desktop-label-bold"); + + HBox statusBox = new HBox(10, weatherLabel, tempLabel, clockLabel, dayLabel, weekLabel); + statusBox.setAlignment(Pos.CENTER_RIGHT); + statusBox.setMinWidth(Region.USE_PREF_SIZE); + + getChildren().addAll( + settingsButton, playerLabel, heartsBox, + spacerLeft, nextWeekButton, spacerRight, + statusBox + ); + } + + private HBox buildHeartsBox(final Player player) { + HBox box = new HBox(4); + box.setAlignment(Pos.CENTER_LEFT); + box.getStyleClass().add("hearts-display"); + + Label[] hearts = new Label[HEART_COUNT]; + for (int i = 0; i < HEART_COUNT; i++) { + hearts[i] = new Label("♥"); + hearts[i].getStyleClass().add("heart-icon"); + box.getChildren().add(hearts[i]); + } + + Runnable refresh = () -> { + int lives = player.getLives(); + for (int i = 0; i < HEART_COUNT; i++) { + boolean active = i < lives; + hearts[i].getStyleClass().removeAll("heart-active", "heart-empty"); + hearts[i].getStyleClass().add(active ? "heart-active" : "heart-empty"); + } + }; + player.getLivesProperty().addListener((obs, ov, nv) -> Platform.runLater(refresh)); + refresh.run(); + + return box; + } + + private Label styledLabel(final String styleClass) { + Label l = new Label(); + l.getStyleClass().add(styleClass); + return l; + } + + public Label getPlayerLabel() { + return playerLabel; + } + + public Label getWeatherLabel() { + return weatherLabel; + } + + public Label getTempLabel() { + return tempLabel; + } + + public Label getDayLabel() { + return dayLabel; + } + + public Label getWeekLabel() { + return weekLabel; + } + + public Label getClockLabel() { + return clockLabel; + } + + public Button getNextWeekButton() { + return nextWeekButton; + } + + public Button getSettingsButton() { + return settingsButton; + } +} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/DesktopView.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/DesktopView.java index 9bd87e0..a669b65 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/DesktopView.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/DesktopView.java @@ -3,6 +3,7 @@ import edu.ntnu.idi.idatt2003.gruppe42.Controller.ViewControllers.DesktopViewController; import edu.ntnu.idi.idatt2003.gruppe42.Model.App; import edu.ntnu.idi.idatt2003.gruppe42.Model.Player; +import edu.ntnu.idi.idatt2003.gruppe42.View.Views.Components.DesktopBottomBar; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -52,7 +53,7 @@ public enum ModalLayer { RAPPORT, WARNING, GAME_OVER } private final Pane gameOverOverlay = createOverlayPane(); private Node gridArea; - private HBox bottomBar; + private DesktopBottomBar bottomBar; private final List popupRoots = new ArrayList<>(); @@ -62,15 +63,6 @@ public enum ModalLayer { RAPPORT, WARNING, GAME_OVER } private final EnumSet activeLayers = EnumSet.noneOf(ModalLayer.class); - private Label weatherLabel; - private Label tempLabel; - private Label dayLabel; - private Label weekLabel; - private Label clockLabel; - private Label playerLabel; - private Button nextWeekButton; - private Button settingsButton; - /** Constructs the desktop view. */ public DesktopView(final DesktopViewController controller) { this.controller = controller; @@ -78,28 +70,28 @@ public DesktopView(final DesktopViewController controller) { this.popupOverlay = new Pane(); this.popupOverlay.setPickOnBounds(false); this.popupOverlay.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); + + gridArea = createGrid(); + bottomBar = new DesktopBottomBar(controller.getPlayer()); + + // Ensure the popup overlay is in a pane that allows x,y positioning, + // and then place that pane in the center of the BorderPane. + Pane centerArea = new Pane(gridArea, popupOverlay); + if (gridArea instanceof Region region) { + region.prefWidthProperty().bind(centerArea.widthProperty()); + region.prefHeightProperty().bind(centerArea.heightProperty()); + } + popupOverlay.prefWidthProperty().bind(centerArea.widthProperty()); + popupOverlay.prefHeightProperty().bind(centerArea.heightProperty()); + + desktopPane.setCenter(centerArea); + desktopPane.setBottom(bottomBar); + desktopPane.getStyleClass().add("desktop-root"); } /** @return the scene root containing the desktop and popup overlay. */ public Pane getRoot() { if (sceneRoot == null) { - gridArea = createGrid(); - bottomBar = createBottomBar(); - - // Ensure the popup overlay is in a pane that allows x,y positioning, - // and then place that pane in the center of the BorderPane. - Pane centerArea = new Pane(gridArea, popupOverlay); - if (gridArea instanceof Region region) { - region.prefWidthProperty().bind(centerArea.widthProperty()); - region.prefHeightProperty().bind(centerArea.heightProperty()); - } - popupOverlay.prefWidthProperty().bind(centerArea.widthProperty()); - popupOverlay.prefHeightProperty().bind(centerArea.heightProperty()); - - desktopPane.setCenter(centerArea); - desktopPane.setBottom(bottomBar); - desktopPane.getStyleClass().add("desktop-root"); - sceneRoot = new Pane(desktopPane); desktopPane.prefWidthProperty().bind(sceneRoot.widthProperty()); desktopPane.prefHeightProperty().bind(sceneRoot.heightProperty()); @@ -318,126 +310,50 @@ private static boolean isDesktopApp(final App app) { && app != App.FILEPICKER; } - private HBox createBottomBar() { - settingsButton = new Button("⚙"); - settingsButton.getStyleClass().add("desktop-settings-button"); - - Player player = controller.getPlayer(); - - playerLabel = new Label("User: " + player.getName()); - playerLabel.getStyleClass().add("desktop-label-bold"); - playerLabel.setMinWidth(Region.USE_PREF_SIZE); - - HBox heartsBox = buildHeartsBox(player); - - nextWeekButton = new Button("Advance to next week"); - nextWeekButton.getStyleClass().add("primary-button"); - - Region spacerLeft = new Region(); - Region spacerRight = new Region(); - HBox.setHgrow(spacerLeft, Priority.ALWAYS); - HBox.setHgrow(spacerRight, Priority.ALWAYS); - - HBox bar = new HBox(15, - settingsButton, playerLabel, heartsBox, - spacerLeft, nextWeekButton, spacerRight, - createStatusBox() - ); - bar.setAlignment(Pos.CENTER_LEFT); - bar.setPadding(new Insets(5, 15, 5, 15)); - bar.getStyleClass().add("desktop-bottom-bar"); - return bar; - } - - private HBox buildHeartsBox(final Player player) { - HBox box = new HBox(4); - box.setAlignment(Pos.CENTER_LEFT); - box.getStyleClass().add("hearts-display"); - - Label[] hearts = new Label[HEART_COUNT]; - for (int i = 0; i < HEART_COUNT; i++) { - hearts[i] = new Label("♥"); - hearts[i].getStyleClass().add("heart-icon"); - box.getChildren().add(hearts[i]); - } - - Runnable refresh = () -> { - int lives = player.getLives(); - for (int i = 0; i < HEART_COUNT; i++) { - boolean active = i < lives; - hearts[i].getStyleClass().removeAll("heart-active", "heart-empty"); - hearts[i].getStyleClass().add(active ? "heart-active" : "heart-empty"); - } - }; - player.getLivesProperty().addListener((obs, ov, nv) -> Platform.runLater(refresh)); - refresh.run(); - - return box; - } - - private HBox createStatusBox() { - weatherLabel = styledLabel("desktop-label"); - tempLabel = styledLabel("desktop-label"); - dayLabel = styledLabel("desktop-label-bold"); - weekLabel = styledLabel("desktop-label-bold"); - clockLabel = styledLabel("desktop-label-bold"); - - HBox box = new HBox(10, weatherLabel, tempLabel, clockLabel, dayLabel, weekLabel); - box.setAlignment(Pos.CENTER_RIGHT); - box.setMinWidth(Region.USE_PREF_SIZE); - return box; - } - - private Label styledLabel(final String styleClass) { - Label l = new Label(); - l.getStyleClass().add(styleClass); - return l; - } - private String resource(final String path) { return getClass().getResource(path).toExternalForm(); } /** Updates player name. */ public void updatePlayerName(final String name) { - Platform.runLater(() -> playerLabel.setText("User: " + name)); + Platform.runLater(() -> bottomBar.getPlayerLabel().setText("User: " + name)); } /** Updates clock. */ public void updateClock(final String time) { - Platform.runLater(() -> clockLabel.setText(time)); + Platform.runLater(() -> bottomBar.getClockLabel().setText(time)); } /** Updates day. */ public void updateDay(final String day, final boolean isSunday) { Platform.runLater(() -> { - dayLabel.setText(day); - nextWeekButton.setVisible(isSunday); + bottomBar.getDayLabel().setText(day); + bottomBar.getNextWeekButton().setVisible(isSunday); }); } /** Updates week. */ public void updateWeek(final String week) { - Platform.runLater(() -> weekLabel.setText(week)); + Platform.runLater(() -> bottomBar.getWeekLabel().setText(week)); } /** Updates weather. */ public void updateWeather(final String weather) { - Platform.runLater(() -> weatherLabel.setText(weather)); + Platform.runLater(() -> bottomBar.getWeatherLabel().setText(weather)); } /** Updates temperature. */ public void updateTemperature(final String temperature) { - Platform.runLater(() -> tempLabel.setText(temperature + "°C")); + Platform.runLater(() -> bottomBar.getTempLabel().setText(temperature + "°C")); } /** @return the next week button. */ public Button getNextWeekButton() { - return nextWeekButton; + return bottomBar.getNextWeekButton(); } /** @return the settings button. */ public Button getSettingsButton() { - return settingsButton; + return bottomBar.getSettingsButton(); } } \ No newline at end of file