diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/PopupsController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/PopupsController.java index c9669c6..9f938a9 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/PopupsController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/PopupsController.java @@ -6,6 +6,7 @@ import edu.ntnu.idi.idatt2003.gruppe42.view.Popup; import java.util.List; import java.util.Optional; +import javafx.application.Platform; import javafx.geometry.Point2D; import javafx.scene.control.Button; import javafx.scene.layout.Pane; @@ -37,11 +38,14 @@ private void initPopup(Popup popup) { makeDraggable(popup); popup.getCloseButton().setOnAction(event -> hide(popup.getType())); - Pane root = popup.getRoot(); - root.parentProperty().addListener((obs, oldP, newP) -> { + popup.getRoot().parentProperty().addListener((obs, oldP, newP) -> { if (newP instanceof Pane parent) { - parent.widthProperty().addListener((o, ov, nv) -> keepInBounds(popup)); - parent.heightProperty().addListener((o, ov, nv) -> keepInBounds(popup)); + parent.widthProperty().addListener(( + o, ov, nv) -> + Platform.runLater(() -> keepInBounds(popup))); + parent.heightProperty().addListener(( + o, ov, nv) -> + Platform.runLater(() -> keepInBounds(popup))); } }); } @@ -61,7 +65,7 @@ public boolean show(App type) { .findFirst() .ifPresent(popup -> { popup.show(); - keepInBounds(popup); + Platform.runLater(() -> keepInBounds(popup)); }); audioManager.playSfx(Audio.OPEN_APP); return true; @@ -105,7 +109,6 @@ private void makeDraggable(Popup popup) { popup.getHeader().setOnMousePressed(event -> { root.layoutXProperty().unbind(); root.layoutYProperty().unbind(); - if (root.getParent() instanceof Pane parent) { Point2D local = parent.sceneToLocal(event.getSceneX(), event.getSceneY()); offset[0] = local.getX() - root.getLayoutX(); @@ -119,15 +122,9 @@ private void makeDraggable(Popup popup) { Point2D local = parent.sceneToLocal(event.getSceneX(), event.getSceneY()); double newX = local.getX() - offset[0]; double newY = local.getY() - offset[1]; - - double width = root.getWidth() > 0 ? root.getWidth() : root.prefWidth(-1); - double height = root.getHeight() > 0 ? root.getHeight() : root.prefHeight(-1); - - double maxX = Math.max(0, parent.getWidth() - width); - double maxY = Math.max(0, parent.getHeight() - height); - - root.setLayoutX(Math.max(0, Math.min(newX, maxX))); - root.setLayoutY(Math.max(0, Math.min(newY, maxY))); + double[] clamped = clamp(root, parent, newX, newY); + root.setLayoutX(clamped[0]); + root.setLayoutY(clamped[1]); } event.consume(); }); @@ -144,17 +141,29 @@ private void keepInBounds(Popup popup) { return; } if (root.getParent() instanceof Pane parent) { - double width = root.getWidth() > 0 ? root.getWidth() : root.prefWidth(-1); - double height = root.getHeight() > 0 ? root.getHeight() : root.prefHeight(-1); - - double maxX = Math.max(0, parent.getWidth() - width); - double maxY = Math.max(0, parent.getHeight() - height); - - root.setLayoutX(Math.max(0, Math.min(root.getLayoutX(), maxX))); - root.setLayoutY(Math.max(0, Math.min(root.getLayoutY(), maxY))); + double[] clamped = clamp(root, parent, root.getLayoutX(), root.getLayoutY()); + root.setLayoutX(clamped[0]); + root.setLayoutY(clamped[1]); } } + /** + * Clamps the given x/y coordinates so the popup stays within its parent bounds. + * + * @param root the popup root pane + * @param parent the parent pane + * @param x the desired x position + * @param y the desired y position + * @return a two-element array containing the clamped x and y values + */ + private double[] clamp(Pane root, Pane parent, double x, double y) { + double width = root.getWidth() > 0 ? root.getWidth() : root.prefWidth(-1); + double height = root.getHeight() > 0 ? root.getHeight() : root.prefHeight(-1); + double maxX = Math.max(0, parent.getWidth() - width); + double maxY = Math.max(0, parent.getHeight() - height); + return new double[]{Math.max(0, Math.min(x, maxX)), Math.max(0, Math.min(y, maxY))}; + } + /** * Adds multiple popups to the controller. * @@ -181,8 +190,8 @@ public void addPopup(Popup popup) { * @return the matching popup, or null if none exists */ public Popup getPopup(App type) { - Optional match = popups.stream().filter(popup -> popup.getType() - .equals(type)).findFirst(); + Optional match = popups.stream().filter( + popup -> popup.getType().equals(type)).findFirst(); return match.orElse(null); } 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 index 0e4016f..104151b 100644 --- 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 @@ -35,7 +35,7 @@ public class MailController implements AppController { public MailController(TimeAndWeatherController timeAndWeatherController) { this.timeAndWeatherController = timeAndWeatherController; unreadCount = Bindings.createIntegerBinding(() -> - (int) messages.stream().filter(Message::isSeen).count(), + (int) messages.stream().filter(m -> !m.isSeen()).count(), messages ); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/appcontrollers/StockAppController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/appcontrollers/StockAppController.java index aafa39f..80a8b12 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/appcontrollers/StockAppController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/appcontrollers/StockAppController.java @@ -97,6 +97,9 @@ public StockAppController( stockApp.getQuantitySpinner().valueProperty().addListener( (obs, old, newValue) -> { + if (isWeekend) { + onMarketClosed.run(); + } if (currentStock != null) { updateReceipt(); } @@ -199,7 +202,6 @@ private void updateTradeUi() { var spinner = stockApp.getQuantitySpinner(); if (isWeekend) { - tradeButton.setDisable(true); tradeButton.getStyleClass().add("trade-button-weekend"); tradeButton.setOpacity(0.45); spinner.setDisable(true); @@ -253,7 +255,7 @@ private void handleTransaction() { } if (value > 0 && currentStock.getSalesPrice().compareTo(BigDecimal.ZERO) <= 0) { - return; // Buying bankrupt stock disabled + return; } BigDecimal quantity = new BigDecimal(Math.abs(value)); @@ -268,10 +270,9 @@ private void handleTransaction() { } } - /** Buys given stock. */ private void handleBuy(final Stock stock, final BigDecimal quantity) throws Exception { Transaction transaction = marketController.getExchange() - .buy(stock.getSymbol(), quantity); + .buy(stock.getSymbol(), quantity); if (transaction != null) { transaction.commit(player); player.getPortfolio().addShare(transaction.getShare()); @@ -279,7 +280,6 @@ private void handleBuy(final Stock stock, final BigDecimal quantity) throws Exce } } - /** Sells given stock. */ private void handleSell(final Stock stock, final BigDecimal quantity) { player.getPortfolio().getShares().stream() .filter(share -> share.getStock().getSymbol().equals(stock.getSymbol())) 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 index f099d48..631ccca 100644 --- 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 @@ -3,30 +3,25 @@ import edu.ntnu.idi.idatt2003.gruppe42.controller.PopupsController; import edu.ntnu.idi.idatt2003.gruppe42.controller.appcontrollers.MailController; import edu.ntnu.idi.idatt2003.gruppe42.model.App; +import java.util.Objects; 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.image.Image; +import javafx.scene.image.ImageView; 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 class for creating desktop user interface components. */ -public final class DesktopComponentFactory { - - private final PopupsController popupsController; - private final MailController mailController; +public record DesktopComponentFactory( + PopupsController popupsController, MailController mailController) { /** * Constructs a DesktopComponentFactory. @@ -34,9 +29,7 @@ public final class DesktopComponentFactory { * @param popupsController the popups controller * @param mailController the mail controller */ - public DesktopComponentFactory(PopupsController popupsController, MailController mailController) { - this.popupsController = popupsController; - this.mailController = mailController; + public DesktopComponentFactory { } /** @@ -46,57 +39,70 @@ public DesktopComponentFactory(PopupsController popupsController, MailController * @return the created application button node */ public Node createAppButton(final App type) { - Button button = new Button(type.getDisplayName()); - button.getStyleClass().add("app-button"); + Button button = new Button(); + button.setStyle( + "-fx-background-color: transparent; -fx-padding: 0; -fx-border-color: transparent;" + ); button.setMinSize(0, 0); - Node appNode = button; - - if (type == App.MAIL) { - final StackPane container = new StackPane(); - - final 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(-10, -10, 0, 0)); - - // Bind container size to button size so badge is aligned to button corners - container.prefWidthProperty().bind(button.prefWidthProperty()); - container.prefHeightProperty().bind(button.prefHeightProperty()); - container.maxWidthProperty().bind(button.prefWidthProperty()); - container.maxHeightProperty().bind(button.prefHeightProperty()); - - appNode = container; + try { + String path = iconPath(type); + Image img = new javafx.scene.image.Image( + Objects.requireNonNull(getClass().getResourceAsStream(Objects.requireNonNull(path)))); + + ImageView icon = new javafx.scene.image.ImageView(img); + icon.setPreserveRatio(false); + icon.fitWidthProperty().bind(button.prefWidthProperty()); + icon.fitHeightProperty().bind(button.prefHeightProperty()); + + button.setGraphic(icon); + button.setPadding(javafx.geometry.Insets.EMPTY); + + button.prefWidthProperty().addListener(( + obs, ov, nv) -> + applyIconClip(icon, nv.doubleValue(), button.getPrefHeight())); + button.prefHeightProperty().addListener(( + obs, ov, nv) -> + applyIconClip(icon, button.getPrefWidth(), nv.doubleValue())); + button.setGraphic(icon); + } catch (Exception e) { + button.setText(type.getDisplayName()); } - final Node finalNode = appNode; - finalNode.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(); - } - }); + button.parentProperty().addListener((obs, oldParent, newParent) -> + bindButtonSize(button, newParent)); popupsController.bindOpenButton(button, type); + setupDrag(button); + return button; + } + + private String iconPath(final App type) { + return switch (type) { + case MAIL -> "/Images/mailApp_icon.png"; + case BANK -> "/Images/bankApp_icon.png"; + case STOCK -> "/Images/stockApp_icon.png"; + case HUSTLERS -> "/Images/hustlersApp_icon.png"; + default -> null; + }; + } + private void bindButtonSize(final Button button, final Object parent) { + if (parent 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(); + } + } + + private void setupDrag(final Button button) { button.setOnDragDetected(event -> { Dragboard dragboard = button.startDragAndDrop(TransferMode.MOVE); SnapshotParameters params = new SnapshotParameters(); @@ -109,12 +115,19 @@ public Node createAppButton(final App type) { dragboard.setContent(content); event.consume(); }); + } - if (type == App.MAIL) { - return appNode; + private void applyIconClip( + final javafx.scene.image.ImageView icon, + final double w, + final double h) { + if (w <= 0 || h <= 0) { + return; } - - return button; + javafx.scene.shape.Rectangle clip = new javafx.scene.shape.Rectangle(w, h); + clip.setArcWidth(w * 0.45); + clip.setArcHeight(h * 0.45); + icon.setClip(clip); } /** @@ -141,4 +154,4 @@ public void configureCellAsDropTarget(final StackPane cell) { event.consume(); }); } -} +} \ No newline at end of file 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 index 7565a80..6781e17 100644 --- 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 @@ -114,7 +114,6 @@ private void wireWeekendRapport() { */ 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", @@ -138,9 +137,8 @@ public void showDebtWarning() { */ public void showMarketClosedWarning() { warningApp.configure( - "🔒", + "It's the WEEKEND!", "Market Closed", - "Trading is suspended on weekends.\nThe market reopens on Monday.", "Got it" ); warningApp.getPrimaryButton().setOnAction(event -> dismissWarning()); @@ -152,7 +150,7 @@ public void showMarketClosedWarning() { */ public void showInsufficientFundsWarning() { warningApp.configure( - "🥺", + "NO MORE MONEY!", "Insufficient Funds", "You don't have enough cash to complete this purchase.", "I'll be back" diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/viewcontrollers/StartViewController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/viewcontrollers/StartViewController.java index ee24e98..fa23133 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/viewcontrollers/StartViewController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/viewcontrollers/StartViewController.java @@ -97,9 +97,8 @@ public StartViewController(final Millions application, final StartView startView startView.getLoginButton().setOnAction(event -> { if (startView.getSelectedMode() == null) { warningApp.configure( - "⚠", + "OBS", "Difficulty Required", - "Choose how much starting money you want before jumping in.", "Got it" ); warningApp.getPrimaryButton().setOnAction(e -> popupsController.hide(App.WARNING)); @@ -172,7 +171,6 @@ private boolean isValidName(final String value) { */ private void showFileWarning() { warningApp.configure( - "⚠", "Invalid File", settingsAppController.getErrorMessage(), "Revert to default file" diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/model/GameFactory.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/model/GameFactory.java index 710f592..d8949ad 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/model/GameFactory.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/model/GameFactory.java @@ -26,9 +26,9 @@ public static Player createPlayer( final String username, final Difficulty difficulty ) { - final BigDecimal easyMoney = new BigDecimal("1000"); - final BigDecimal mediumMoney = new BigDecimal("100"); - final BigDecimal hardMoney = new BigDecimal("0"); + final BigDecimal easyMoney = new BigDecimal("10000"); + final BigDecimal mediumMoney = new BigDecimal("1000"); + final BigDecimal hardMoney = new BigDecimal("100"); BigDecimal money = switch (difficulty) { case EASY -> easyMoney; 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 3f74d0d..8d28e55 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 @@ -15,6 +15,8 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; @@ -69,7 +71,28 @@ protected Popup(int width, int height, int x, int y, App type) { scrollPane.getStyleClass().add("popup-scroll-pane"); scrollPane.setFitToWidth(true); scrollPane.setFitToHeight(false); - scrollPane.setPrefSize(width, height); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + content.heightProperty().addListener((obs, ov, nv) -> + scrollPane.setVbarPolicy( + nv.doubleValue() > scrollPane.getHeight() + ? ScrollPane.ScrollBarPolicy.AS_NEEDED + : ScrollPane.ScrollBarPolicy.NEVER + ) + ); + scrollPane.heightProperty().addListener((obs, ov, nv) -> + scrollPane.setVbarPolicy( + content.getHeight() > nv.doubleValue() + ? ScrollPane.ScrollBarPolicy.AS_NEEDED + : ScrollPane.ScrollBarPolicy.NEVER + ) + ); + if (width > 0) { + scrollPane.setPrefWidth(width); + } + if (height > 0) { + scrollPane.setPrefHeight(height); + } closeButton = buildCloseButton(); header = buildHeader(closeButton, type.getDisplayName()); @@ -83,7 +106,6 @@ protected Popup(int width, int height, int x, int y, App type) { Rectangle borderOverlay = buildBorderOverlay(); wrapper = new Pane(root, borderOverlay); - // Bind wrapper size to root's size so centering logic works wrapper.prefWidthProperty().bind(root.widthProperty()); wrapper.prefHeightProperty().bind(root.heightProperty()); borderOverlay.widthProperty().bind(root.widthProperty()); @@ -96,7 +118,6 @@ protected Popup(int width, int height, int x, int y, App type) { wrapper.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> wrapper.toFront()); loadStylesheets(); - } private Rectangle buildClip(BorderPane target) { @@ -127,7 +148,6 @@ private void loadStylesheets() { sheets.add("/css/popup.css"); sheets.add("/css/components.css"); - // Map App to CSS String appCss = switch (type) { case BANK -> "/css/bank.css"; case MAIL -> "/css/mail.css"; @@ -142,7 +162,6 @@ private void loadStylesheets() { sheets.add(appCss); } - // Some apps need receipt.css if (type == App.STOCK || type == App.WEEKENDREPORT) { sheets.add("/css/receipt.css"); } @@ -169,11 +188,12 @@ private HBox buildHeader(Button closeButton, String title) { Label titleLabel = new Label(title); titleLabel.getStyleClass().add("popup-title"); - HBox titleContainer = new HBox(titleLabel); - titleContainer.setAlignment(Pos.CENTER); + StackPane titleContainer = new StackPane(titleLabel); + StackPane.setAlignment(titleLabel, Pos.CENTER); + titleContainer.setMinHeight(Region.USE_PREF_SIZE); HBox.setHgrow(titleContainer, Priority.ALWAYS); - HBox hbox = new HBox(10, closeButton, titleContainer); + HBox hbox = new HBox(closeButton, titleContainer); hbox.setAlignment(Pos.CENTER_LEFT); hbox.setPadding(new Insets(5, 10, 5, 10)); hbox.getStyleClass().add("popup-header"); @@ -192,11 +212,10 @@ private String resource(final String path) { public void centerInParent() { Runnable bind = () -> { if (wrapper.getParent() instanceof Pane pane) { - // Use root.widthProperty/heightProperty for centering wrapper.layoutXProperty().bind( - pane.widthProperty().subtract(root.widthProperty()).divide(2)); + pane.widthProperty().subtract(wrapper.prefWidthProperty()).divide(2)); wrapper.layoutYProperty().bind( - pane.heightProperty().subtract(root.heightProperty()).divide(2)); + pane.heightProperty().subtract(wrapper.prefHeightProperty()).divide(2)); } }; bind.run(); @@ -238,5 +257,4 @@ public HBox getHeader() { public Button getCloseButton() { return closeButton; } - } \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/FilePickerApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/FilePickerApp.java index 26d9b3c..bff9896 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/FilePickerApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/FilePickerApp.java @@ -90,7 +90,6 @@ private void buildLayout() { quickLabel.getStyleClass().add("fp-sidebar-section"); sidebar.getChildren().add(quickLabel); - String[] icons = { "🏠", "🖥", "📄", "⬇" }; String[] names = { "Home", "Desktop", "Documents", "Downloads" }; for (int i = 0; i < QUICK_ACCESS.length; i++) { File dir = QUICK_ACCESS[i]; @@ -99,7 +98,7 @@ private void buildLayout() { continue; } - Button btn = new Button(icons[i] + " " + names[i]); + Button btn = new Button(names[i]); btn.getStyleClass().add("fp-sidebar-item"); btn.setMaxWidth(Double.MAX_VALUE); btn.setAlignment(Pos.CENTER_LEFT); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/HustlersApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/HustlersApp.java index b29031c..5271734 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/HustlersApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/HustlersApp.java @@ -46,7 +46,7 @@ public class HustlersApp extends Popup { * including sidebar navigation and content rendering system.

*/ public HustlersApp() { - super(720, 480, 400, 400, App.HUSTLERS); + super(720, 480, 400, 200, App.HUSTLERS); buildChapters(); buildUi(); show(); 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 a5d356c..8931529 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 @@ -75,6 +75,7 @@ private VBox createMailItem(Message message) { Label senderLabel = new Label(message.getAuthor()); senderLabel.setMinWidth(0); + senderLabel.setWrapText(true); senderLabel.getStyleClass().add("mail-sender"); Region spacer = new Region(); @@ -98,7 +99,7 @@ private VBox createMailItem(Message message) { item.getChildren().addAll(top, subLabel, contentLabel); - if (message.isSeen()) { + if (!message.isSeen()) { HBox bottom = new HBox(); bottom.setAlignment(Pos.CENTER_RIGHT); Button seenButton = new Button("Mark as Read"); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/SettingsApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/SettingsApp.java index 2f5b4e1..dd79a08 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/SettingsApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/SettingsApp.java @@ -51,7 +51,7 @@ public record GradientConfig(Color colorA, Color colorB, String direction) {} * @param isLoggedIn whether the user is logged in */ public SettingsApp(final boolean isLoggedIn) { - super(400, 500, 80, 400, App.SETTINGS); + super(400, 0, 80, 400, App.SETTINGS); this.isLoggedIn = isLoggedIn; openFileChooserButton = new Button("Import stock file"); @@ -319,7 +319,7 @@ public static String buildCssGradient( case "Right to Left" -> "to left"; case "Top Left to Bottom Right" -> "to bottom right"; case "Top Right to Bottom Left" -> "to bottom left"; - default -> "to bottom"; // "Top → Bottom" + default -> "to bottom"; }; return String.format( "-fx-background-color: linear-gradient(%s, %s, %s);", diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/StockApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/StockApp.java index c0fbfde..03d20e8 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/StockApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/StockApp.java @@ -146,10 +146,13 @@ public void showStockPage(final Stock stock) { content.setSpacing(20); content.getChildren().setAll(topControls, headerBox, priceLabel, graph, receiptCentered); - } - /** Updates the price label. */ + /** + * Updates the price label with the current sales price of the given stock. + * + * @param stock the stock whose price should be displayed + */ public void updatePriceLabel(final Stock stock) { if (priceLabel != null) { Platform.runLater(() -> diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WarningApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WarningApp.java index 37e958d..2e93cbc 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WarningApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/WarningApp.java @@ -8,6 +8,8 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; @@ -21,7 +23,6 @@ */ public final class WarningApp extends Popup { - private final Label iconLabel = new Label(); private final Label titleLabel = new Label(); private final Label messageLabel = new Label(); private final Button primaryButton = new Button(); @@ -35,10 +36,7 @@ public final class WarningApp extends Popup { * and can be configured dynamically using {@link #configure}. */ public WarningApp() { - super(370, 270, 0, 0, App.WARNING); - - iconLabel.setStyle("-fx-font-size: 30px;"); - iconLabel.setAlignment(Pos.CENTER); + super(0, 0, 0, 0, App.WARNING); titleLabel.getStyleClass().add("warning-title"); titleLabel.setWrapText(true); @@ -51,14 +49,29 @@ public WarningApp() { messageLabel.setAlignment(Pos.CENTER); messageLabel.setTextAlignment(TextAlignment.CENTER); messageLabel.setMaxWidth(Double.MAX_VALUE); + messageLabel.setMinHeight(Region.USE_PREF_SIZE); ScrollPane messageScroll = new ScrollPane(messageLabel); messageScroll.setFitToWidth(true); messageScroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); - messageScroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED); - messageScroll.setPrefHeight(200); + messageLabel.heightProperty().addListener((obs, ov, nv) -> + messageScroll.setVbarPolicy( + nv.doubleValue() > messageScroll.getHeight() + ? ScrollPane.ScrollBarPolicy.AS_NEEDED + : ScrollPane.ScrollBarPolicy.NEVER + ) + ); + messageScroll.heightProperty().addListener((obs, ov, nv) -> + messageScroll.setVbarPolicy( + messageLabel.getHeight() > nv.doubleValue() + ? ScrollPane.ScrollBarPolicy.AS_NEEDED + : ScrollPane.ScrollBarPolicy.NEVER + ) + ); + messageScroll.setMaxHeight(200); messageScroll.getStyleClass().add("warning-scroll"); messageScroll.setStyle("-fx-background-color: transparent; -fx-background: transparent;"); + VBox.setVgrow(messageScroll, Priority.NEVER); primaryButton.getStyleClass().add("primary-button"); primaryButton.setPrefWidth(150); @@ -69,8 +82,9 @@ public WarningApp() { HBox buttons = new HBox(12, secondaryButton, primaryButton); buttons.setAlignment(Pos.CENTER); - VBox inner = new VBox(10, iconLabel, titleLabel, messageScroll, buttons); + VBox inner = new VBox(10, titleLabel, messageScroll, buttons); inner.setAlignment(Pos.CENTER); + inner.setMinWidth(150); content.setPadding(new Insets(24, 28, 24, 28)); content.setAlignment(Pos.CENTER); @@ -80,18 +94,15 @@ public WarningApp() { /** * Configures the warning popup with a single primary action button. * - * @param icon the icon/emoji displayed at the top of the popup * @param title the title of the warning * @param message the detailed warning message * @param primaryLabel the label for the primary action button */ public void configure( - final String icon, final String title, final String message, final String primaryLabel ) { - iconLabel.setText(icon); titleLabel.setText(title); messageLabel.setText(message); primaryButton.setText(primaryLabel); @@ -107,20 +118,17 @@ public void configure( /** * Configures the warning popup with both primary and secondary actions. * - * @param icon the icon/emoji displayed at the top of the popup * @param title the title of the warning * @param message the detailed warning message * @param secondaryLabel the label for the secondary/cancel button * @param primaryLabel the label for the primary action button */ public void configure( - final String icon, final String title, final String message, final String secondaryLabel, final String primaryLabel ) { - iconLabel.setText(icon); titleLabel.setText(title); messageLabel.setText(message); primaryButton.setText(primaryLabel); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/StartView.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/StartView.java index d23c8b8..3c9f4fc 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/StartView.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/views/StartView.java @@ -79,9 +79,9 @@ public BorderPane getRoot() { startingMoneyOptions.setAlignment(Pos.CENTER); // Starting Money Options - RadioButton easyCheck = new RadioButton("Easy ($1000)"); - RadioButton mediumCheck = new RadioButton("Medium ($100)"); - RadioButton hardCheck = new RadioButton("Hard ($0)"); + RadioButton easyCheck = new RadioButton("Easy ($10,000)"); + RadioButton mediumCheck = new RadioButton("Medium ($1000)"); + RadioButton hardCheck = new RadioButton("Hard ($100)"); easyCheck.getStyleClass().add("difficulty-label"); mediumCheck.getStyleClass().add("difficulty-label"); hardCheck.getStyleClass().add("difficulty-label"); 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 index 4029551..96585c7 100644 --- 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 @@ -1,14 +1,22 @@ package edu.ntnu.idi.idatt2003.gruppe42.view.views.components; import edu.ntnu.idi.idatt2003.gruppe42.model.Player; +import javafx.animation.Animation; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; 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.effect.DropShadow; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; +import javafx.util.Duration; /** * Bottom status bar for the desktop view. @@ -61,11 +69,6 @@ public DesktopBottomBar(Player 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"); @@ -76,11 +79,25 @@ public DesktopBottomBar(Player player) { statusBox.setAlignment(Pos.CENTER_RIGHT); statusBox.setMinWidth(Region.USE_PREF_SIZE); - getChildren().addAll( - settingsButton, playerLabel, playerStatusLabel, heartsBox, - spacerLeft, nextWeekButton, spacerRight, - statusBox - ); + HBox leftGroup = new HBox(10, settingsButton, playerLabel, playerStatusLabel, heartsBox); + leftGroup.setAlignment(Pos.CENTER_LEFT); + HBox.setHgrow(leftGroup, Priority.ALWAYS); + + HBox rightGroup = new HBox(10, statusBox); + rightGroup.setAlignment(Pos.CENTER_RIGHT); + HBox.setHgrow(rightGroup, Priority.ALWAYS); + + HBox sidesRow = new HBox(leftGroup, rightGroup); + sidesRow.setMaxWidth(Double.MAX_VALUE); + HBox.setHgrow(sidesRow, Priority.ALWAYS); + + addGlowAnimation(nextWeekButton); + StackPane.setAlignment(nextWeekButton, Pos.CENTER); + + StackPane centerPane = new StackPane(sidesRow, nextWeekButton); + HBox.setHgrow(centerPane, Priority.ALWAYS); + + getChildren().add(centerPane); } /** @@ -117,6 +134,29 @@ private HBox buildHeartsBox(final Player player) { return box; } + /** + * Adds a pulsing glow animation to the given button to attract user attention. + * + * @param button the button to animate + */ + private void addGlowAnimation(final Button button) { + DropShadow glow = new DropShadow(); + glow.setColor(Color.web("#4fc3f7")); + glow.setRadius(0); + button.setEffect(glow); + + Timeline timeline = new Timeline( + new KeyFrame(Duration.ZERO, + new KeyValue(glow.radiusProperty(), 0)), + new KeyFrame(Duration.seconds(0.9), + new KeyValue(glow.radiusProperty(), 18)), + new KeyFrame(Duration.seconds(1.8), + new KeyValue(glow.radiusProperty(), 0)) + ); + timeline.setCycleCount(Animation.INDEFINITE); + timeline.play(); + } + /** * Creates a label with the given CSS style class. * @@ -164,4 +204,4 @@ public Button getNextWeekButton() { public Button getSettingsButton() { return settingsButton; } -} +} \ No newline at end of file diff --git a/src/main/resources/Images/bankApp_icon.png b/src/main/resources/Images/bankApp_icon.png new file mode 100644 index 0000000..642b9a4 Binary files /dev/null and b/src/main/resources/Images/bankApp_icon.png differ diff --git a/src/main/resources/Images/hustlersApp_icon.png b/src/main/resources/Images/hustlersApp_icon.png new file mode 100644 index 0000000..30d19ee Binary files /dev/null and b/src/main/resources/Images/hustlersApp_icon.png differ diff --git a/src/main/resources/Images/mailApp_icon.png b/src/main/resources/Images/mailApp_icon.png new file mode 100644 index 0000000..e1be4fd Binary files /dev/null and b/src/main/resources/Images/mailApp_icon.png differ diff --git a/src/main/resources/Images/stockApp_icon.png b/src/main/resources/Images/stockApp_icon.png new file mode 100644 index 0000000..093366a Binary files /dev/null and b/src/main/resources/Images/stockApp_icon.png differ diff --git a/src/main/resources/Images/windows7_login_background.jpg b/src/main/resources/Images/windows7_login_background.jpg deleted file mode 100644 index 258f2df..0000000 Binary files a/src/main/resources/Images/windows7_login_background.jpg and /dev/null differ diff --git a/src/main/resources/css/mail.css b/src/main/resources/css/mail.css index 8069bdc..3a32cb4 100644 --- a/src/main/resources/css/mail.css +++ b/src/main/resources/css/mail.css @@ -24,7 +24,6 @@ -fx-font-weight: bold; -fx-font-size: 14px; -fx-text-fill: #2c3e50; - -fx-text-overrun: ellipsis; } .mail-time { diff --git a/src/main/resources/stocks.csv b/src/main/resources/stocks.csv index a771bb5..111804f 100644 --- a/src/main/resources/stocks.csv +++ b/src/main/resources/stocks.csv @@ -1,31 +1,31 @@ # Companies by Market Cap # Ticker,Name,Price -BAN,Banana Inc.,191.27 -APL,Apple Inc.,276.43 -TIR,Tire Inc.,404.68 -OIL,Oil Inc.,204.62 -SPC,Space Org.,311.20 -WND,Turbine Inc.,311.62 -NUC,Nuclear Inc.,343.35 -CAR,Car Inc.,426.52 -HYD,Hydro Inc.,501.05 -TIP,Q-tip Inc.,128.75 -TED,Teddy Bear Inc.,1014.43 -UNI,NTNU Org.,311.14 -COM,Communication Org.,155.28 -MON,Monsters Inc.,329.54 -AIR,Plane Inc.,240.70 -ATB,Bus Inc.,539.52 -BAT,Battery Inc.,243.43 -KWI,Kiwi Inc.,102.24 -VAC,Vacine Inc.,156.76 -JZZ,Jazz Inc.,230.40 -MET,Metal Inc.,302.89 -FRM,Farm Org.,260.76 -COS,Cosmetic Inc.,403.30 -TXI,Taxi Org.,239.70 -GOR,Gorilla Inc.,503.35 -MKY,Monkey Inc.,67.87 -NET,Network Org.,389.37 -WWW,Internet Org.,402.50 \ No newline at end of file +BAN,Banana Inc.,73.45 +APL,Apple Inc.,189.32 +TIR,Tire Inc.,445.20 +OIL,Oil Inc.,312.88 +SPC,Space Org.,876.54 +WND,Turbine Inc.,234.17 +NUC,Nuclear Inc.,567.90 +CAR,Car Inc.,389.45 +HYD,Hydro Inc.,623.78 +TIP,Q-tip Inc.,52.33 +TED,Teddy Bear Inc.,10000.00 +UNI,NTNU Org.,145.67 +COM,Communication Org.,289.54 +MON,Monsters Inc.,478.23 +AIR,Plane Inc.,334.89 +ATB,Bus Inc.,712.45 +BAT,Battery Inc.,198.76 +KWI,Kiwi Inc.,85.43 +VAC,Vacine Inc.,167.32 +JZZ,Jazz Inc.,423.67 +MET,Metal Inc.,534.21 +FRM,Farm Org.,278.90 +COS,Cosmetic Inc.,356.78 +TXI,Taxi Org.,201.45 +GOR,Gorilla Inc.,489.67 +MKY,Monkey Inc.,93.21 +NET,Network Org.,645.33 +WWW,Internet Org.,812.56 \ No newline at end of file diff --git a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/transaction/TransactionArchiveTest.java b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/transaction/TransactionArchiveTest.java index 0d61c75..9502a4d 100644 --- a/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/transaction/TransactionArchiveTest.java +++ b/src/test/java/edu/ntnu/idi/idatt2003/gruppe42/model/transaction/TransactionArchiveTest.java @@ -27,7 +27,7 @@ void setUp() { Stock stock = new Stock("AAPL", "Apple Inc.", new BigDecimal("100")); Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("90")); purchase = new Purchase(share, 1); - sale = new Sale(share, 2); + sale = new Sale(share, 2); } @Test