Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b9ac442
chore: Add CSS styles.
pawelsa May 10, 2026
1341261
feat: Add StockComponent class
pawelsa May 10, 2026
eaf5c7b
feat: Create initial Exchange View
pawelsa May 10, 2026
a16eb70
feat: Add Exchange View to SceneFactory and redirect from Start View
pawelsa May 10, 2026
f42927d
feat: Implement MVC model to the Exchange view. Add interactivity wit…
pawelsa May 11, 2026
0bb1037
feat: Add Exchange SceneFactory instance
pawelsa May 11, 2026
8a40566
chore: Make StockComponent only accept stock context for interactivity.
pawelsa May 11, 2026
43abf8f
feat: Implement initial MVC for StockView.
pawelsa May 11, 2026
04ffd92
feat: Add StockView to SceneFactory.
pawelsa May 11, 2026
9bf2142
feat(UIFactory): Add initialization with labels (observer purposes).
pawelsa May 11, 2026
af9a328
feat: Made barebone StockView implementation.
pawelsa May 11, 2026
3946e13
refactor: UIElementCompositor -> UICompositor. More flexible structure.
pawelsa May 11, 2026
6480da6
chore: Cleanup imports.
pawelsa May 11, 2026
964ad44
feat: Add small text to css.
pawelsa May 11, 2026
6501d66
enchancement: StockView structure.
pawelsa May 11, 2026
1ad05e3
feat(Portfolio, Share): Add methods for diverse calculations.
pawelsa May 11, 2026
3be9854
fix(AbstractViewUI): Resize navigation bar
pawelsa May 11, 2026
6a20ced
refactor: StockView cleanup
pawelsa May 11, 2026
1988645
fix: Latest change percent calculation.
pawelsa May 11, 2026
526c1fb
feat: Add TextValueComponent class
pawelsa May 12, 2026
4ddf525
refactor(UICompositor): more flexibility
pawelsa May 12, 2026
0d3bbf8
refactor: StockView MVC model to be more structured.
pawelsa May 12, 2026
99fce05
feat(CssUtils): Add small details for simpler observable objects colo…
pawelsa May 12, 2026
732d0c4
remove: Label coloring method.
pawelsa May 12, 2026
363251f
enchancement: StockView color displaying
pawelsa May 12, 2026
8b44171
refactor(StockComponent): Utilize color displaying /w builder
pawelsa May 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/model/market/Stock.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.ntnu.idi.idatt.model.market;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
Expand Down Expand Up @@ -90,6 +91,16 @@ public BigDecimal getLatestPriceChange() {
return prices.get(size - 1).subtract(prices.get(size - 2));
}

// TODO: JAVADOCS, JUNIT
public BigDecimal getLatestPriceChangePercent() {
if (prices.isEmpty() || prices.size() == 1) {
return BigDecimal.ZERO;
}

return (getLatestPriceChange().divide(prices.get(prices.size() - 2), 2, RoundingMode.HALF_UP))
.multiply(new BigDecimal("100"));
}

/**
* Getter for current sale price
*
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/model/portfolio/Portfolio.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ public List<Share> getShares(String symbol) {
return shares.stream().filter(s -> s.getStock().getSymbol().equals(symbol)).toList();
}

// TODO: JAVADOCS, JUNIT
public BigDecimal getOwnedAmount(String symbol) {
return getShares(symbol).stream().map(s -> s.getQuantity())
.reduce(BigDecimal.ZERO, BigDecimal::add);
}

public BigDecimal getProfitFromStock(String symbol) {
return getShares(symbol).stream().map(s -> s.getProfit())
.reduce(BigDecimal.ZERO, BigDecimal::add);
}

public double getChangeFromStock(String symbol) {
BigDecimal profitTotal = getProfitFromStock(symbol);
BigDecimal costTotal = getShares(symbol).stream().map(s -> s.getPurchasePrice())
.reduce(BigDecimal.ZERO, BigDecimal::add);

return (profitTotal.subtract(costTotal).doubleValue()) / 100;
}

/**
* Method for checking if the portfolio contains a specific share
*
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/model/portfolio/Share.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.math.BigDecimal;

import edu.ntnu.idi.idatt.model.market.Stock;
import edu.ntnu.idi.idatt.service.transaction.SaleCalculator;

/**
* Share class
Expand Down Expand Up @@ -50,4 +51,9 @@ public BigDecimal getPurchasePrice() {
return purchasePrice;
}

// TODO: JAVADOCS, JUNIT
public BigDecimal getProfit() {
return new SaleCalculator(this).calculateGross().subtract(purchasePrice);
}

}
32 changes: 32 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package edu.ntnu.idi.idatt.view;

import edu.ntnu.idi.idatt.model.market.Stock;
import edu.ntnu.idi.idatt.view.entry.StartController;
import edu.ntnu.idi.idatt.view.entry.StartModel;
import edu.ntnu.idi.idatt.view.entry.StartView;
import edu.ntnu.idi.idatt.view.primary.exchange.ExchangeController;
import edu.ntnu.idi.idatt.view.primary.exchange.ExchangeModel;
import edu.ntnu.idi.idatt.view.primary.exchange.ExchangeView;
import edu.ntnu.idi.idatt.view.primary.stock.StockController;
import edu.ntnu.idi.idatt.view.primary.stock.StockModel;
import edu.ntnu.idi.idatt.view.primary.stock.StockView;
import javafx.scene.Parent;

public class SceneFactory {
Expand All @@ -20,4 +27,29 @@ public static Parent createStartView() {

}

public static Parent createExchangeView() {

ExchangeModel model = new ExchangeModel();
ExchangeView view = new ExchangeView();
ExchangeController controller = new ExchangeController(model);

view.setModel(model);
view.setController(controller);

return view.getInstance();

}

public static Parent createStockView(Stock stock) {

StockModel model = new StockModel();
StockView view = new StockView();
StockController controller = new StockController(model, stock);

view.setModel(model);
view.setController(controller);

return view.getInstance();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void createUIComponents() {
navigation = new VBox();
navigation.setMaxHeight(Double.MAX_VALUE);
navigation.getStyleClass().add("dark");
navigation.setMaxWidth(150);
navigation.setMaxWidth(200);

header = new HBox();
header.getStyleClass().add("light");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package edu.ntnu.idi.idatt.view.components.elements;

import java.math.BigDecimal;
import java.util.List;
import java.util.function.Consumer;

import edu.ntnu.idi.idatt.model.market.Stock;
import edu.ntnu.idi.idatt.view.components.ui.UICompositor;
import edu.ntnu.idi.idatt.view.util.CssUtils;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;

public class StockComponent extends HBox {

private final Stock stock;

public StockComponent(Stock stock) {
this.stock = stock;
this.setMaxSize(Double.MAX_VALUE, 100);
this.setPadding(new Insets(40));
this.setAlignment(Pos.BASELINE_CENTER);

this.setStyle("-fx-background-color: #404950;");

Label title = new Label(this.getTitle());

Label latestText = new Label("Latest");
Label latestValue = new Label(stock.getSalesPrice().toString() + "USD");

Label changeText = new Label("Change");
Label changeValue = new Label(stock.getLatestPriceChange().toString() + "USD");
Label changeValuePercent = new Label(
String.valueOf(stock.getLatestPriceChangePercent()) + "%");

List<Label> labels = List.of(latestText, latestValue, changeText, changeValue, changeValuePercent);

CssUtils.apply(title, CssUtils.BIG_TEXT_32);
labels.forEach(l -> CssUtils.apply(l, CssUtils.MED_TEXT_16));

String color = CssUtils.generateValueColors(stock.getLatestPriceChange().doubleValue());
CssUtils.apply(changeValue, color);
CssUtils.apply(changeValuePercent, color);

UICompositor stockComponent = new UICompositor.Builder()
.parent(new HBox())
.growWithAlignment(Pos.CENTER)
.addContent(title)
.filler()
.filler()
.filler()
.wrap(new VBox())
.addAllContent(latestText, latestValue)
.unwrap()
.filler()
.wrap(new VBox())
.addAllContent(changeText, changeValue, changeValuePercent)
.unwrap()
.build();

this.getChildren().addAll(stockComponent.makeUI());
}

public String getTitle() {
return stock.getCompany() + " (" + stock.getSymbol() + ")";
}

public String getSymbol() {
return stock.getSymbol();
}

public void onStockClick(Consumer<String> handler) {
this.setOnMouseClicked((e) -> handler.accept(this.getSymbol()));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package edu.ntnu.idi.idatt.view.components.elements;

import edu.ntnu.idi.idatt.view.util.CssUtils;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;

public class TextValueComponent extends HBox {

private final SimpleStringProperty value = new SimpleStringProperty();
private final SimpleStringProperty color = new SimpleStringProperty();

public TextValueComponent(String prefix) {
Label prefixLabel = new Label(prefix);
Label valueLabel = new Label();
valueLabel.textProperty().bind(value);
CssUtils.apply(prefixLabel, CssUtils.MED_TEXT_16);
CssUtils.apply(valueLabel, CssUtils.MED_TEXT_16);

color.addListener((obs, oldVal, newVal) -> {
CssUtils.apply(valueLabel, newVal);
});

this.getChildren().addAll(prefixLabel, valueLabel);
}

public SimpleStringProperty valueProperty() {
return value;
}

public SimpleStringProperty colorProperty() {
return color;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import javafx.geometry.Pos;
import javafx.scene.Parent;
Expand All @@ -11,24 +12,22 @@
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;

public class UIElementCompositor {
public class UICompositor {

private final Pane parent;
private final List<Parent> elements;

public UIElementCompositor(Builder builder) {
public UICompositor(Builder builder) {
this.parent = builder.parent;
this.elements = builder.elements;
}

public Parent makeUI() {
parent.getChildren().addAll(elements);
return parent;
}

public static class Builder {
private Pane parent;
private ArrayList<Parent> elements = new ArrayList<>();
private ArrayList<Pane> wrappers = new ArrayList<>();
private boolean isWrapper = false;

public Builder parent(Pane parent) {
this.parent = parent;
Expand All @@ -49,35 +48,56 @@ public Builder growWithAlignment(Pos position) {
return this;
}

public Builder setPrefSize(double width, double height) {
parent.setPrefSize(width, height);
public Builder addContent(Parent child) {
Pane root = isWrapper ? wrappers.getLast() : parent;
root.getChildren().add(child);
return this;
}

public Builder addContent(Parent parent) {
elements.add(parent);
public Builder addAllContent(Parent... children) {
Pane root = isWrapper ? wrappers.getLast() : parent;
root.getChildren().addAll(List.of(children));
return this;
}

public Builder addAllContent(Parent... parents) {
elements.addAll(List.of(parents));
public Builder parentProperties(Consumer<Pane> handle) {
handle.accept(this.parent);
return this;
}

public Builder wrap(Pane wrapper) {
isWrapper = true;
wrappers.add(wrapper);
return this;
}

public Builder unwrap() {
isWrapper = false;
parent.getChildren().add(wrappers.getLast());
return this;
}

public Builder wrapperProperties(Consumer<Pane> handle) {
handle.accept(wrappers.getLast());
return this;
}

public Builder filler() {
Pane root = isWrapper ? wrappers.getLast() : parent;

Region filler = new Region();
if (parent instanceof HBox) {
if (root instanceof HBox) {
HBox.setHgrow(filler, Priority.ALWAYS);
}
if (parent instanceof VBox) {
if (root instanceof VBox) {
VBox.setVgrow(filler, Priority.ALWAYS);
}
elements.add(filler);
root.getChildren().add(filler);
return this;
}

public UIElementCompositor build() {
return new UIElementCompositor(this);
public UICompositor build() {
return new UICompositor(this);
}

}
Expand Down
Loading
Loading