Skip to content

Commit

Permalink
implimented events with stocks and mail app
Browse files Browse the repository at this point in the history
  • Loading branch information
peretr committed May 18, 2026
1 parent fb928e7 commit b4a21ed
Show file tree
Hide file tree
Showing 12 changed files with 308 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers;

import edu.ntnu.idi.idatt2003.gruppe42.Controller.TimeAndWeatherController;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Day;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Message;
import javafx.beans.binding.Bindings;
Expand All @@ -14,22 +15,27 @@
public class MailController implements AppController {
private final ObservableList<Message> messages = FXCollections.observableArrayList();
private final IntegerBinding unreadCount;
private final TimeAndWeatherController timeAndWeatherController;
private int week;
private String day;
private int time;

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

// Ensure unreadCount updates when a message's seen property changes
messages.addListener((ListChangeListener<Message>) c -> {
while (c.next()) {
if (c.wasAdded()) {
for (Message m : c.getAddedSubList()) {
m.seenProperty().addListener((obs, oldVal, newVal) -> unreadCount.invalidate());
messages.addListener((ListChangeListener<Message>) change -> {
while (change.next()) {
if (change.wasAdded()) {
for (Message message : change.getAddedSubList()) {
message.seenProperty().addListener((obs, oldVal, newVal) -> unreadCount.invalidate());
}
}
}
Expand All @@ -51,22 +57,25 @@ public IntegerBinding unreadCountProperty() {
return unreadCount;
}


public void createMessage(String author, String title, String message) {
updateDateTime();
messages.add(0, new Message(author, title, message, week, day, time));
}

/**
* 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
* Clears all messages from the inbox.
*/
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));
public void clearMessages() {
messages.clear();
}
public void updateDateTime() {
week = timeAndWeatherController.weekIndexProperty().get();
day = timeAndWeatherController.getDayOfWeekString();
time = timeAndWeatherController.hourProperty().get();
}

@Override
public void nextTick() {
// No specific tick logic currently needed for mail
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,39 @@ protected void updateItem(final Stock stock, final boolean empty) {
/** Sets weekend status. */
public void setWeekend(final boolean weekend) {
this.isWeekend = weekend;
Platform.runLater(this::updateTradeUI);
}

/** Updates the Trade button and spinner state. */
private void updateTradeUI() {
Button tradeButton = stockApp.getConfirmButton();
if (weekend) {
var spinner = stockApp.getQuantitySpinner();

if (isWeekend) {
tradeButton.setDisable(true);
tradeButton.getStyleClass().add("trade-button-weekend");
tradeButton.setOpacity(0.45);
stockApp.getQuantitySpinner().setDisable(true);
spinner.setDisable(true);
return;
}

spinner.setDisable(false);
tradeButton.getStyleClass().remove("trade-button-weekend");

if (currentStock != null) {
boolean isBankrupt = currentStock.getSalesPrice().compareTo(BigDecimal.ZERO) <= 0;
boolean isBuying = spinner.getValue() > 0;

if (isBankrupt && isBuying) {
tradeButton.setDisable(true);
tradeButton.setOpacity(0.5);
} else {
tradeButton.setDisable(false);
tradeButton.setOpacity(1.0);
}
} else {
tradeButton.getStyleClass().remove("trade-button-weekend");
tradeButton.setDisable(false);
tradeButton.setOpacity(1.0);
stockApp.getQuantitySpinner().setDisable(false);
}
}

Expand Down Expand Up @@ -189,6 +213,11 @@ private void handleTransaction() {
if (value == 0) {
return;
}

if (value > 0 && currentStock.getSalesPrice().compareTo(BigDecimal.ZERO) <= 0) {
return; // Buying bankrupt stock disabled
}

BigDecimal quantity = new BigDecimal(Math.abs(value));
if (value > 0) {
try{
Expand Down Expand Up @@ -237,12 +266,13 @@ private void handleSell(final Stock stock, final BigDecimal quantity) {
/** Updates receipt view. */
private void updateReceipt() {
if (currentStock != null) {
Platform.runLater(() ->
Platform.runLater(() -> {
stockApp.getReceipt().update(
currentStock,
new BigDecimal(stockApp.getQuantitySpinner().getValue())
)
);
);
updateTradeUI();
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ public PopupsController(List<Popup> popups) {
private void initPopup(Popup popup) {
makeDraggable(popup);
popup.getCloseButton().setOnAction(event -> hide(popup.getType()));

// Keep in bounds when parent resizes
Pane root = popup.getRoot();
root.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));
}
});
}

/** Shows a popup. */
Expand All @@ -34,7 +43,10 @@ public boolean show(App type) {
popups.stream()
.filter(popup -> popup.getType().equals(type))
.findFirst()
.ifPresent(Popup::show);
.ifPresent(popup -> {
popup.show();
keepInBounds(popup);
});
audioManager.playSFX(Audio.OPEN_APP);
return true;
}
Expand Down Expand Up @@ -62,6 +74,9 @@ private void makeDraggable(Popup popup) {
double[] offset = new double[2];

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 @@ -75,15 +90,37 @@ 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 maxX = parent.getWidth() - root.getWidth();
double maxY = parent.getHeight() - root.getHeight();

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)));
}
event.consume();
});
}

private void keepInBounds(Popup popup) {
Pane root = popup.getRoot();
if (root.layoutXProperty().isBound()) {
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)));
}
}

/** Adds popups. */
public void addPopups(List<Popup> popups) {
popups.forEach(this::addPopup);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,118 @@
package edu.ntnu.idi.idatt2003.gruppe42.Controller;

import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.MailController;
import edu.ntnu.idi.idatt2003.gruppe42.Model.EventBus;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Stock;

import edu.ntnu.idi.idatt2003.gruppe42.Model.StockEvent;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class StockController {
private Stock stock;
private int timeframe;

private static final int STEEP_THRESHOLD = 46;
private static final int EVENT_MAGNITUDE = 90;
private static final double NOISE_RANGE = 8.0;
private static final int MIN_TREND_FRAMES = 10;

private final Stock stock;

private int trendTimeframe;
private int trendFramesRemaining;
private int trend;
private int delay;
private int time;
private Random random;
private double timeMultiplier;
private BigDecimal price;
private BigDecimal startPrice;
private BigDecimal offset;
private BigDecimal priceChange;

private BigDecimal pendingShock;

public StockController(Stock stock) {
public StockController(final Stock stock) {
this.stock = stock;
time = 0;
random = new Random();
timeMultiplier = (random.nextDouble() + 0.1) / 20;
startPrice = stock.getSalesPrice();

price = startPrice;
priceChange = new BigDecimal(random.nextInt(startPrice.intValue()/2));
this.time = 0;
this.delay = 0;
this.random = new Random();
this.startPrice = stock.getSalesPrice();
this.price = startPrice;
nextTrend();
}

public BigDecimal updatePrice() {
BigDecimal marketFluctuation = BigDecimal.valueOf(Math.sin(timeMultiplier * time))
.multiply(priceChange)
.add(startPrice);
BigDecimal noise = BigDecimal.valueOf((random.nextDouble() * 2 - 1) * NOISE_RANGE);

if (isDelay()) {
delay--;
if (delay == 0 && pendingShock != null) {
price = price.add(pendingShock);
pendingShock = null;
}

if (price.compareTo(BigDecimal.ZERO) > 0) {
price = price.add(noise);
}

if (price.compareTo(BigDecimal.ZERO) < 0) {
price = BigDecimal.ZERO;
}
return price.setScale(2, RoundingMode.HALF_UP);
}

if (price.compareTo(BigDecimal.ZERO) > 0 || (isTrajectorySteep() && trend > 0)) {
BigDecimal trendStep = BigDecimal.valueOf(trend)
.divide(BigDecimal.valueOf(trendTimeframe), 6, RoundingMode.HALF_UP);

double speculatorFluctuation = random.nextDouble(2) - 1;
offset = BigDecimal.valueOf(speculatorFluctuation*30);
price = price.add(trendStep).add(noise);
}

time += 1;
price = marketFluctuation;
if (price.compareTo(BigDecimal.ZERO) < 0) {
price = BigDecimal.ZERO;
}

return price.add(offset);
trendFramesRemaining--;
time++;

if (trendFramesRemaining <= 0) {
nextTrend();
}

return price.setScale(2, RoundingMode.HALF_UP);
}

public void handleEvent() {
private void nextTrend() {
trend = random.nextInt(101) - 50;
trendTimeframe = random.nextInt(120) + MIN_TREND_FRAMES;
trendFramesRemaining = trendTimeframe;

if (isTrajectorySteep()) {
prepareEvent();
delay = 20;
} else {
delay = 0;
pendingShock = null;
}
}

private void prepareEvent() {
int direction = trend > 0 ? 1 : -1;
double scaledMagnitude = EVENT_MAGNITUDE * ((double) Math.abs(trend) / 50.0);
pendingShock = BigDecimal.valueOf(direction * scaledMagnitude);
EventBus.get()
.publish(new StockEvent(stock.getCompany(), trend));
}

public boolean isTrajectorySteep() {
return true;
private boolean isTrajectorySteep() {
return Math.abs(trend) > STEEP_THRESHOLD;
}

public boolean isDelay() {
private boolean isDelay() {
return delay > 0;
}
}

public BigDecimal getPrice() {
return price;
}

}
Loading

0 comments on commit b4a21ed

Please sign in to comment.