From 00765f5e3600d8185af171798d8b5f3dec657e19 Mon Sep 17 00:00:00 2001 From: pawelsa Date: Sun, 10 May 2026 13:29:37 +0200 Subject: [PATCH 1/9] feat: add UIElementCompositor class for UI building. --- .../components/ui/UIElementCompositor.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIElementCompositor.java diff --git a/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIElementCompositor.java b/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIElementCompositor.java new file mode 100644 index 0000000..0fed8e6 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIElementCompositor.java @@ -0,0 +1,85 @@ +package edu.ntnu.idi.idatt.view.components.ui; + +import java.util.ArrayList; +import java.util.List; + +import javafx.geometry.Pos; +import javafx.scene.Parent; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; + +public class UIElementCompositor { + + private final Pane parent; + private final List elements; + + public UIElementCompositor(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 elements = new ArrayList<>(); + + public Builder parent(Pane parent) { + this.parent = parent; + return this; + } + + public Builder growWithAlignment(Pos position) { + if (parent instanceof HBox) { + ((HBox) parent).setAlignment(position); + parent.setMaxWidth(Double.MAX_VALUE); + HBox.setHgrow(parent, Priority.ALWAYS); + } + if (parent instanceof VBox) { + ((VBox) parent).setAlignment(position); + parent.setMaxHeight(Double.MAX_VALUE); + VBox.setVgrow(parent, Priority.ALWAYS); + } + return this; + } + + public Builder setPrefSize(double width, double height) { + parent.setPrefSize(width, height); + return this; + } + + public Builder addContent(Parent parent) { + elements.add(parent); + return this; + } + + public Builder addAllContent(Parent... parents) { + elements.addAll(List.of(parents)); + return this; + } + + public Builder filler() { + Region filler = new Region(); + if (parent instanceof HBox) { + HBox.setHgrow(filler, Priority.ALWAYS); + } + if (parent instanceof VBox) { + VBox.setVgrow(filler, Priority.ALWAYS); + } + elements.add(filler); + return this; + } + + public UIElementCompositor build() { + return new UIElementCompositor(this); + } + + } + +} From 6bf236f8280071cd822293109f79d468df2b3f88 Mon Sep 17 00:00:00 2001 From: pawelsa Date: Sun, 10 May 2026 13:30:04 +0200 Subject: [PATCH 2/9] refactor: MainView to utilize the new mechanism. --- .../idatt/view/components/AbstractViewUI.java | 16 ++-- .../ntnu/idi/idatt/view/primary/MainView.java | 95 ++++++++++--------- 2 files changed, 62 insertions(+), 49 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt/view/components/AbstractViewUI.java b/src/main/java/edu/ntnu/idi/idatt/view/components/AbstractViewUI.java index 1b7b889..1933e3b 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/components/AbstractViewUI.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/AbstractViewUI.java @@ -15,6 +15,7 @@ public abstract class AbstractViewUI extends AbstractView { private VBox navigation; private HBox header; private HBox toolbar; + private VBox menu; private SimpleBooleanProperty isMenuVisible = new SimpleBooleanProperty(false); public AbstractViewUI() { @@ -35,8 +36,7 @@ public AbstractViewUI() { this.setInstance(new StackPane()); - Parent menu = createMenu(); - StackPane.setAlignment(menu, Pos.CENTER_RIGHT); + menu.getChildren().add(createMenu()); menu.setVisible(false); Region disableMenu = new Region(); @@ -59,16 +59,20 @@ public void createUIComponents() { navigation = new VBox(); navigation.setMaxHeight(Double.MAX_VALUE); navigation.getStyleClass().add("dark"); - navigation.setPrefWidth(150); + navigation.setMaxWidth(150); header = new HBox(); header.getStyleClass().add("light"); - header.setPrefHeight(80); + header.setMaxHeight(80); toolbar = new HBox(); toolbar.getStyleClass().add("light"); - HBox.setHgrow(toolbar, Priority.ALWAYS); - toolbar.setPrefHeight(80); + toolbar.setMaxHeight(80); + + menu = new VBox(); + StackPane.setAlignment(menu, Pos.CENTER_RIGHT); + menu.getStyleClass().add("dark"); + menu.setMaxWidth(300); } public abstract Parent createContent(); diff --git a/src/main/java/edu/ntnu/idi/idatt/view/primary/MainView.java b/src/main/java/edu/ntnu/idi/idatt/view/primary/MainView.java index d3a151d..b6187ec 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/primary/MainView.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/primary/MainView.java @@ -4,14 +4,13 @@ import edu.ntnu.idi.idatt.view.components.AbstractViewUI; import edu.ntnu.idi.idatt.view.components.elements.IconComponent; import edu.ntnu.idi.idatt.view.components.elements.SearchBarComponent; +import edu.ntnu.idi.idatt.view.components.ui.UIElementCompositor; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Parent; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; @@ -26,89 +25,99 @@ public Parent createContent() { @Override public Parent createNavigation() { - VBox navigation = new VBox(); Label title = new Label("Title"); - title.getStyleClass().add("big-text-32"); - Label newspaper = new Label(" • Newspaper"); + + title.getStyleClass().add("big-text-32"); newspaper.getStyleClass().add("med-text-16"); - navigation.getChildren().addAll(title, newspaper); - return navigation; + UIElementCompositor navigation = new UIElementCompositor.Builder() + .parent(new VBox()) + .addAllContent(title, newspaper) + .build(); + + return navigation.makeUI(); } @Override public Parent createHeader() { - this.getHeader().setAlignment(Pos.CENTER); SearchBarComponent bar = new SearchBarComponent("Search after stocks.."); - bar.onSearchQuery(() -> System.out.println(bar.getQuery())); + HBox.setMargin(bar, new Insets(20)); - return bar; + + UIElementCompositor header = new UIElementCompositor.Builder() + .parent(new HBox()) + .growWithAlignment(Pos.CENTER) + .filler() + .addContent(bar) + .filler() + .build(); + + bar.onSearchQuery(() -> System.out.println(bar.getQuery())); + + return header.makeUI(); } @Override public Parent createToolbar() { - HBox bar = new HBox(); - bar.setAlignment(Pos.CENTER); - bar.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(bar, Priority.ALWAYS); - Label balance = new Label(" Money: " + UserSession.getInstance().getPlayer().getMoney().toString() + " USD"); - balance.getStyleClass().add("med-text-16"); + Label playerStatus = new Label("Status:" + UserSession.getInstance().getPlayer().getStatus()); + Label playerNetWorth = new Label( + "Net Worth: " + UserSession.getInstance().getPlayer().getNetWorth().toString() + " USD"); - Region filler = new Region(); - HBox.setHgrow(filler, Priority.ALWAYS); - - VBox infoWrapper = new VBox(); - infoWrapper.setAlignment(Pos.CENTER); - - Label playerStatus = new Label(); - playerStatus.setText("Status:" + UserSession.getInstance().getPlayer().getStatus()); + balance.getStyleClass().add("med-text-16"); playerStatus.getStyleClass().add("med-text-16"); - - Label playerNetWorth = new Label(); - playerNetWorth.setText("Net Worth: " + UserSession.getInstance().getPlayer().getNetWorth().toString() + " USD"); playerNetWorth.getStyleClass().add("med-text-16"); + VBox infoWrapper = new VBox(); + infoWrapper.setAlignment(Pos.CENTER); infoWrapper.getChildren().addAll(playerStatus, playerNetWorth); - HBox iconWrapper = new HBox(); - iconWrapper.setAlignment(Pos.CENTER); - Image userImg = new Image(this.getClass().getResource("/icons/user.png").toExternalForm()); Image quitImg = new Image(this.getClass().getResource("/icons/quit.png").toExternalForm()); IconComponent userIcon = new IconComponent(userImg, null, 44); IconComponent quitIcon = new IconComponent(quitImg, null, 44); + HBox iconWrapper = new HBox(); + iconWrapper.setAlignment(Pos.CENTER); + iconWrapper.getChildren().addAll(userIcon, quitIcon); + + UIElementCompositor toolbar = new UIElementCompositor.Builder() + .parent(new HBox()) + .growWithAlignment(Pos.CENTER) + .addContent(balance) + .filler() + .addAllContent(infoWrapper, iconWrapper) + .build(); + userIcon.onIconClick(() -> toggleMenu()); - iconWrapper.getChildren().addAll(userIcon, quitIcon); + return toolbar.makeUI(); - bar.getChildren().addAll(balance, filler, infoWrapper, iconWrapper); - return bar; } @Override public Parent createMenu() { - VBox menu = new VBox(); - menu.setMaxSize(300, Double.MAX_VALUE); - menu.getStyleClass().add("dark"); - menu.setAlignment(Pos.TOP_CENTER); Image menuImg = new Image(this.getClass().getResource("/icons/user.png").toExternalForm()); - IconComponent menuIcon = new IconComponent(menuImg, "Account", 60); - menuIcon.getStyleClass().add("big-text-32"); + IconComponent menuIcon = new IconComponent(menuImg, "Account", 60); Label portfolio = new Label(" • Portfolio"); - portfolio.getStyleClass().add("med-text-16"); - Label transactions = new Label(" • Transactions"); + + menuIcon.getStyleClass().add("big-text-32"); + portfolio.getStyleClass().add("med-text-16"); transactions.getStyleClass().add("med-text-16"); - menu.getChildren().addAll(menuIcon, portfolio, transactions); + UIElementCompositor menu = new UIElementCompositor.Builder() + .parent(new VBox()) + .growWithAlignment(Pos.TOP_CENTER) + .setPrefSize(300, Double.MAX_VALUE) + .addAllContent(menuIcon, portfolio, transactions) + .build(); - return menu; + return menu.makeUI(); } } From 8d113105caacdf4daf83bcb2eb1860969b3e2e60 Mon Sep 17 00:00:00 2001 From: pawelsa Date: Sun, 10 May 2026 19:47:09 +0200 Subject: [PATCH 3/9] feat: Implement SceneFactory class for simplyfing scene managing. --- .../edu/ntnu/idi/idatt/view/SceneFactory.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java diff --git a/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java b/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java new file mode 100644 index 0000000..c68f6b2 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java @@ -0,0 +1,23 @@ +package edu.ntnu.idi.idatt.view; + +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 javafx.scene.Parent; + +public class SceneFactory { + + public static Parent createStartView() { + + StartModel model = new StartModel(); + StartView view = new StartView(); + StartController controller = new StartController(model); + + view.setModel(model); + view.setController(controller); + + return view.getInstance(); + + } + +} From adc899ccc015f62fc0c5a5cdc635b7493eee015a Mon Sep 17 00:00:00 2001 From: pawelsa Date: Sun, 10 May 2026 19:47:35 +0200 Subject: [PATCH 4/9] refactor: Launching of primary scene. --- src/main/java/edu/ntnu/idi/idatt/Launcher.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/main/java/edu/ntnu/idi/idatt/Launcher.java b/src/main/java/edu/ntnu/idi/idatt/Launcher.java index 9fc9145..18c49a6 100644 --- a/src/main/java/edu/ntnu/idi/idatt/Launcher.java +++ b/src/main/java/edu/ntnu/idi/idatt/Launcher.java @@ -1,9 +1,7 @@ package edu.ntnu.idi.idatt; +import edu.ntnu.idi.idatt.view.SceneFactory; import edu.ntnu.idi.idatt.view.SceneManager; -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 javafx.application.Application; import javafx.stage.Stage; @@ -24,14 +22,7 @@ public void start(Stage stage) { stage.setHeight(700); stage.setTitle("Stock Game"); - StartModel model = new StartModel(); - StartView view = new StartView(); - StartController controller = new StartController(model); - - view.setModel(model); - view.setController(controller); - - SceneManager.init(stage, view.getInstance()); + SceneManager.init(stage, SceneFactory.createStartView()); stage.show(); } From 1c3246e86fa661203c8282c5a7ec96fbfef8457c Mon Sep 17 00:00:00 2001 From: pawelsa Date: Sun, 10 May 2026 19:48:21 +0200 Subject: [PATCH 5/9] feat: Implement UIFactory for repetative UI creation. --- .../idatt/view/components/ui/UIFactory.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java diff --git a/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java b/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java new file mode 100644 index 0000000..48a08e4 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java @@ -0,0 +1,140 @@ +package edu.ntnu.idi.idatt.view.components.ui; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import edu.ntnu.idi.idatt.session.UserSession; +import edu.ntnu.idi.idatt.view.components.elements.IconComponent; +import edu.ntnu.idi.idatt.view.components.elements.SearchBarComponent; +import edu.ntnu.idi.idatt.view.components.primitives.ActionEventHandler; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Parent; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; + +public class UIFactory { + + public static Parent createHeader(String placeholder, Consumer onSearchQuery) { + SearchBarComponent bar = new SearchBarComponent(placeholder); + + HBox.setMargin(bar, new Insets(20)); + + UIElementCompositor header = new UIElementCompositor.Builder() + .parent(new HBox()) + .growWithAlignment(Pos.CENTER) + .filler() + .addContent(bar) + .filler() + .build(); + + bar.onSearchQuery(() -> onSearchQuery.accept(bar.getQuery())); // Remake? + + return header.makeUI(); + } + + public static Parent createNavigation(String title, List buttonLables, ActionEventHandler... handlers) { + if (buttonLables.size() != handlers.length) { + System.out.println("Failed to build navigation!"); + } + + Label titleLabel = new Label(title); + titleLabel.getStyleClass().add("big-text-32"); + + ArrayList