Skip to content

Commit

Permalink
Merge pull request #141 from einaskoi/per/bugFixes
Browse files Browse the repository at this point in the history
fixed usertested bugs
  • Loading branch information
einaskoi authored May 20, 2026
2 parents 2704375 + b533803 commit 7c713f5
Show file tree
Hide file tree
Showing 24 changed files with 267 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)));
}
});
}
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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();
});
Expand All @@ -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.
*
Expand All @@ -181,8 +190,8 @@ public void addPopup(Popup popup) {
* @return the matching popup, or null if none exists
*/
public Popup getPopup(App type) {
Optional<Popup> match = popups.stream().filter(popup -> popup.getType()
.equals(type)).findFirst();
Optional<Popup> match = popups.stream().filter(
popup -> popup.getType().equals(type)).findFirst();
return match.orElse(null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ public StockAppController(

stockApp.getQuantitySpinner().valueProperty().addListener(
(obs, old, newValue) -> {
if (isWeekend) {
onMarketClosed.run();
}
if (currentStock != null) {
updateReceipt();
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand All @@ -268,18 +270,16 @@ 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());
player.updateStatus();
}
}

/** Sells given stock. */
private void handleSell(final Stock stock, final BigDecimal quantity) {
player.getPortfolio().getShares().stream()
.filter(share -> share.getStock().getSymbol().equals(stock.getSymbol()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,33 @@
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.
*
* @param popupsController the popups controller
* @param mailController the mail controller
*/
public DesktopComponentFactory(PopupsController popupsController, MailController mailController) {
this.popupsController = popupsController;
this.mailController = mailController;
public DesktopComponentFactory {
}

/**
Expand All @@ -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();
Expand All @@ -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);
}

/**
Expand All @@ -141,4 +154,4 @@ public void configureCellAsDropTarget(final StackPane cell) {
event.consume();
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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());
Expand All @@ -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"
Expand Down
Loading

0 comments on commit 7c713f5

Please sign in to comment.