From 17c04d92a76587a664becf6e17da62352f217286 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 20:48:07 +0200
Subject: [PATCH 01/24] Added LanguageManager
Acts as a manager that sets the language to be used by JavaFX, swaps the strings associated with UI elements and returns observables to watch for language changes.
---
.../gruppe53/service/LanguageManager.java | 96 +++++++++++++++++++
1 file changed, 96 insertions(+)
create mode 100644 millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java
diff --git a/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java
new file mode 100644
index 0000000..09b9494
--- /dev/null
+++ b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java
@@ -0,0 +1,96 @@
+package no.ntnu.gruppe53.service;
+
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.StringBinding;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+/**
+ * Manages the language used in the user-interface.
+ */
+public class LanguageManager {
+
+ /**
+ * Sets a default language and creates an observable {@link ResourceBundle}.
+ */
+ private final ObjectProperty bundleProperty =
+ new SimpleObjectProperty<>();
+
+ private LanguageManager() {
+ setLocale(LanguageRegistry.getDefaultLocale());
+ }
+
+ /**
+ * Static Inner Class to lazily create the instance
+ */
+ private static class LanguageManagerInner {
+ private static final LanguageManager INSTANCE =
+ new LanguageManager();
+ }
+
+ /**
+ * Returns the instance of the language manager
+ *
+ * @return the instance of the language manager
+ */
+ public static LanguageManager getInstance() {
+ return LanguageManagerInner.INSTANCE;
+ }
+
+ /**
+ * Returns the observable resource bundle.
+ *
+ * @return an observable resource bundle
+ */
+ public ObjectProperty getBundleProperty() {
+ return bundleProperty;
+ }
+
+ /**
+ * Sets the locale of the instance to a given locale which determines the properties file to load
+ * values from.
+ *
+ * @param locale the locale to change to
+ */
+ public void setLocale(Locale locale) {
+ ResourceBundle bundle =
+ ResourceBundle.getBundle("i18n.lang", locale);
+
+ bundleProperty.set(bundle);
+ }
+
+ /**
+ * Returns the string value of the given key in a .properties file.
+ *
+ * @param key the key to look for in the .properties file
+ * @return the value of the key
+ */
+ public String getString(String key) {
+ return bundleProperty.get().getString(key);
+ }
+
+ /**
+ * Returns the current locale.
+ *
+ * @return the current locale
+ */
+ public Locale getLocale() {
+ return bundleProperty.get().getLocale();
+ }
+
+ /**
+ * Creates a {@link StringBinding} to a given string and associates it with a given key in a .properties file
+ *
+ * @param key the key to associate the string binding with
+ * @return a string binding observable
+ */
+ public StringBinding bindString(String key) {
+ return Bindings.createStringBinding(
+ () -> getString(key),
+ getBundleProperty()
+ );
+ }
+}
\ No newline at end of file
From d987e024af2d0ae055c4659e371a1cfc9085e5de Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 20:48:54 +0200
Subject: [PATCH 02/24] Added LanguageManagerTest
Test class for LanguageManager.
---
.../gruppe53/service/LanguageManagerTest.java | 68 +++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 millions/src/test/java/no/ntnu/gruppe53/service/LanguageManagerTest.java
diff --git a/millions/src/test/java/no/ntnu/gruppe53/service/LanguageManagerTest.java b/millions/src/test/java/no/ntnu/gruppe53/service/LanguageManagerTest.java
new file mode 100644
index 0000000..4f04ae2
--- /dev/null
+++ b/millions/src/test/java/no/ntnu/gruppe53/service/LanguageManagerTest.java
@@ -0,0 +1,68 @@
+package no.ntnu.gruppe53.service;
+
+import javafx.beans.binding.StringBinding;
+import javafx.beans.property.ObjectProperty;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Constructor;
+import java.util.Locale;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class LanguageManagerTest {
+
+ @Test
+ void constructorShouldBePrivate() throws Exception {
+ Constructor constructor =
+ LanguageManager.class.getDeclaredConstructor();
+
+ assertTrue(java.lang.reflect.Modifier.isPrivate(constructor.getModifiers()));
+ }
+
+ @Test
+ void instanceShouldBeUniform() {
+ var lm1 = LanguageManager.getInstance();
+ var lm2 = LanguageManager.getInstance();
+
+ assertEquals(lm1, lm2, "The instance should be unchanged.");
+ }
+
+ @Test
+ void shouldLoadStringsCorrectly() {
+ LanguageManager manager = LanguageManager.getInstance();
+
+ manager.setLocale(new Locale("en"));
+
+ String value = manager.getString("hello");
+
+ assertEquals("Hello world", value);
+ }
+
+ @Test
+ void shouldReturnCorrectLocale() {
+ LanguageManager manager = LanguageManager.getInstance();
+
+ manager.setLocale(new Locale("en"));
+
+ assertEquals(new Locale("en"), manager.getLocale());
+ }
+
+ @Test
+ void getBundlePropertyShouldBeObservable() {
+ assertInstanceOf(ObjectProperty.class, LanguageManager.getInstance().getBundleProperty());
+ }
+
+ @Test
+ void bindStringShouldBeReactiveOnLocaleChange() {
+ LanguageManager lm = LanguageManager.getInstance();
+
+ lm.setLocale(new Locale("en"));
+ StringBinding binding = lm.bindString("hello");
+
+ assertEquals("Hello world", binding.get(), "Should return english version of key.");
+
+ lm.setLocale(new java.util.Locale("no"));
+
+ assertEquals("Hallo verden", binding.get(), "Should return norwegian version of key.");
+ }
+}
\ No newline at end of file
From 4c8fd8849003d95d6168deb04d5914d9daf36ed3 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 20:50:00 +0200
Subject: [PATCH 03/24] Added lang_en.properties
Properties test file for the english language.
---
millions/src/test/resources/i18n/lang_en.properties | 1 +
1 file changed, 1 insertion(+)
create mode 100644 millions/src/test/resources/i18n/lang_en.properties
diff --git a/millions/src/test/resources/i18n/lang_en.properties b/millions/src/test/resources/i18n/lang_en.properties
new file mode 100644
index 0000000..6d5d7ab
--- /dev/null
+++ b/millions/src/test/resources/i18n/lang_en.properties
@@ -0,0 +1 @@
+hello = Hello world
\ No newline at end of file
From 83de3476b51fa80c1db3deb01ebc34eae6509f65 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 20:50:24 +0200
Subject: [PATCH 04/24] Added lang_no.properties
Properties test file for the norwegian language.
---
millions/src/test/resources/i18n/lang_no.properties | 1 +
1 file changed, 1 insertion(+)
create mode 100644 millions/src/test/resources/i18n/lang_no.properties
diff --git a/millions/src/test/resources/i18n/lang_no.properties b/millions/src/test/resources/i18n/lang_no.properties
new file mode 100644
index 0000000..05bdb92
--- /dev/null
+++ b/millions/src/test/resources/i18n/lang_no.properties
@@ -0,0 +1 @@
+hello = Hallo verden
\ No newline at end of file
From 61b0244595e1860e2361bd9569f55e94241a1ecb Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 20:51:43 +0200
Subject: [PATCH 05/24] Added lang.properties
Default properties file that loads if target locale was not found.
---
.../src/main/resources/i18n/lang.properties | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 millions/src/main/resources/i18n/lang.properties
diff --git a/millions/src/main/resources/i18n/lang.properties b/millions/src/main/resources/i18n/lang.properties
new file mode 100644
index 0000000..ac7a7dd
--- /dev/null
+++ b/millions/src/main/resources/i18n/lang.properties
@@ -0,0 +1,62 @@
+# Default file to use if there is an error with language locale
+
+# StartView
+newGame = New Game
+languageButtonLabel = Toggle Language
+quitGame = Quit Game
+
+# PlayerNameChooserView
+playerNameConfirmButton = Confirm
+playerNameInfo = Please enter your player name:
+
+# PlayerStartingMoneyChooserView
+playerStartingMoneyConfirmButton = Confirm
+playerStartingMoneyInfoLabel = Select your starting money balance for trading:
+
+# CSVChooserView
+csvHeader = CSV File Required
+csvContent = Please select a .csv file of the stocks to be used for the exchange in the next window.\n\nIf none is selected (or if .csv file is empty/malformed), a default file will be used.\n\nThe file needs to be formated as: StockTicker,CorporationName,PriceTypeDouble.\n\nExample: NVDA,Nvidia,191.27\n\n'#' signifies comments, and along with lines not formatted correctly, will be ignored.
+csvFileChooserTitle = Select CSV File
+csvFileFilterLabel = CSV Files
+
+# NavigationBar
+navMoneyLabelText = Money:
+navNetWorthLabelText = Net Worth:
+navPlayerLabelText = Player:
+marketButton = Market
+portfolioButton = Portfolio
+historyButton = History
+
+# FooterBar
+currentWeekLabelText = Current Week:
+advanceWeekButton = Advance Week
+
+# MarketView
+marketTitle = Stock Market
+xAxisLabel = Week
+yAxisLabel = Price
+selectedStock = Selected Stock:
+purchaseQuantity = Quantity:
+purchaseGross = Gross:
+purchaseCommission = Commission:
+purchaseTotal = Total:
+purchaseButton = Purchase
+stockCellFormat=%s - %s | Price: $%s | Price change: %s | Highest price: %s | Lowest price: %s
+
+# PortfolioView
+portfolioTitle = Portfolio
+selectedShare = Selected Share:
+portfolioGross = Gross:
+portfolioCommission = Commission:
+portfolioTax = Tax:
+portfolioTotal = Total:
+sellButton = Sell
+portfolioShareQuantity = Qty:
+portfolioBuyPrice = Buy price:
+portfolioCurrentPrice = Current Price:
+
+# TransactionArchiveView
+historyTitle=Transaction History
+historyTypePurchase=Purchase
+historyTypeSale=Sale
+historyCellFormat=Week %d | %s | %s | Qty: %s | Total: $%s
From e5927c979cb8b86765943b5d5b136e19e4645f93 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 20:52:09 +0200
Subject: [PATCH 06/24] Added lang_en.properties
Properties file for the english language.
---
.../main/resources/i18n/lang_en.properties | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 millions/src/main/resources/i18n/lang_en.properties
diff --git a/millions/src/main/resources/i18n/lang_en.properties b/millions/src/main/resources/i18n/lang_en.properties
new file mode 100644
index 0000000..3e162bc
--- /dev/null
+++ b/millions/src/main/resources/i18n/lang_en.properties
@@ -0,0 +1,62 @@
+# File for the english language
+
+# StartView
+newGame = New Game
+languageButtonLabel = Toggle Language
+quitGame = Quit Game
+
+# PlayerNameChooserView
+playerNameConfirmButton = Confirm
+playerNameInfo = Please enter your player name:
+
+# PlayerStartingMoneyChooserView
+playerStartingMoneyConfirmButton = Confirm
+playerStartingMoneyInfoLabel = Select your starting money balance for trading:
+
+# CSVChooserView
+csvHeader = CSV File Required
+csvContent = Please select a .csv file of the stocks to be used for the exchange in the next window.\n\nIf none is selected (or if .csv file is empty/malformed), a default file will be used.\n\nThe file needs to be formated as: StockTicker,CorporationName,PriceTypeDouble.\n\nExample: NVDA,Nvidia,191.27\n\n'#' signifies comments, and along with lines not formatted correctly, will be ignored.
+csvFileChooserTitle = Select CSV File
+csvFileFilterLabel = CSV Files
+
+# NavigationBar
+navMoneyLabelText = Money:
+navNetWorthLabelText = Net Worth:
+navPlayerLabelText = Player:
+marketButton = Market
+portfolioButton = Portfolio
+historyButton = History
+
+# FooterBar
+currentWeekLabelText = Current Week:
+advanceWeekButton = Advance Week
+
+# MarketView
+marketTitle = Stock Market
+xAxisLabel = Week
+yAxisLabel = Price
+selectedStock = Selected Stock:
+purchaseQuantity = Quantity:
+purchaseGross = Gross:
+purchaseCommission = Commission:
+purchaseTotal = Total:
+purchaseButton = Purchase
+stockCellFormat=%s - %s | Price: $%s | Price change: %s | Highest price: %s | Lowest price: %s
+
+# PortfolioView
+portfolioTitle = Portfolio
+selectedShare = Selected Share:
+portfolioGross = Gross:
+portfolioCommission = Commission:
+portfolioTax = Tax:
+portfolioTotal = Total:
+sellButton = Sell
+portfolioShareQuantity = Qty:
+portfolioBuyPrice = Buy price:
+portfolioCurrentPrice = Current Price:
+
+# TransactionArchiveView
+historyTitle=Transaction History
+historyTypePurchase=Purchase
+historyTypeSale=Sale
+historyCellFormat=Week %d | %s | %s | Qty: %s | Total: $%s
From bc20399ff73a2c2f6af03e9a90abd92334e1e642 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 20:52:26 +0200
Subject: [PATCH 07/24] Added lang_en.properties
Properties file for the norwegian language.
---
.../main/resources/i18n/lang_no.properties | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 millions/src/main/resources/i18n/lang_no.properties
diff --git a/millions/src/main/resources/i18n/lang_no.properties b/millions/src/main/resources/i18n/lang_no.properties
new file mode 100644
index 0000000..63261ec
--- /dev/null
+++ b/millions/src/main/resources/i18n/lang_no.properties
@@ -0,0 +1,62 @@
+# File for the norwegian language
+
+# StartView
+newGame = Nytt Spill
+languageButtonLabel = Skift Språk
+quitGame = Avslutt
+
+# PlayerNameChooserView
+playerNameConfirmButton = Bekreft
+playerNameInfo = Vennligst skriv spillernavnet ditt:
+
+# PlayerStartingMoneyChooserView
+playerStartingMoneyConfirmButton = Bekreft
+playerStartingMoneyInfoLabel = Velg startsaldo for aksjehandel:
+
+# CSVChooserView
+csvHeader = CSV Fil Påkrevd
+csvContent = Vennligst velg en .csv fil som inneholder aksjene som skal brukes på børsen i neste vindu. \n\nDersom ingen fil velges (eller .csv filen er tom eller har feil struktur), vil en standard fil bli brukt. \n\nFilen må formates slik: Aksjesymbol,FirmaNavn,PrisTypeDobbel. \n\nEksempel: NVDA,Nvidia,191.27\n\n'#' brukes for kommentarer, og som med feilformaterte linjer, vil slike linjer bli ignorert.
+csvFileChooserTitle = Velg CSV fil
+csvFileFilterLabel = CSV Filer
+
+# NavigationBar
+navMoneyLabelText = Saldo:
+navNetWorthLabelText = Nettoformue:
+navPlayerLabelText = Spiller:
+marketButton = Marked
+portfolioButton = Portefølje
+historyButton = Historikk
+
+# FooterBar
+currentWeekLabelText = Gjeldende Uke:
+advanceWeekButton = Neste Uke
+
+# MarketView
+marketTitle = Aksjemarked
+xAxisLabel = Uke
+yAxisLabel = Pris
+selectedStock = Valgt aksje:
+purchaseQuantity = Antall:
+purchaseGross = Brutto:
+purchaseCommission = Kurtasje:
+purchaseTotal = Total:
+purchaseButton = Kjøp
+stockCellFormat=%s - %s | Pris: $%s | Prisendring: %s | Høyeste pris: %s | Laveste pris: %s
+
+# PortfolioView
+portfolioTitle = Portefølje
+selectedShare = Valgt andel:
+portfolioGross = Brutto:
+portfolioCommission = Kurtasje:
+portfolioTax = Skatt:
+portfolioTotal = Total:
+sellButton = Selg
+portfolioShareQuantity = Ant:
+portfolioBuyPrice = Kjøpspris:
+portfolioCurrentPrice = Gjeldende pris:
+
+# TransactionArchiveView
+historyTitle=Transaksjonshistorikk
+historyTypePurchase=Kjøp
+historyTypeSale=Salg
+historyCellFormat=Uke %d | %s | %s | Ant: %s | Total: $%s
\ No newline at end of file
From 6618ef3c72b311180fd9dc94bcc390c250f6710c Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 20:53:03 +0200
Subject: [PATCH 08/24] Added LanguageOption
Act as a datacarrier for language.
---
.../no/ntnu/gruppe53/service/LanguageOption.java | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 millions/src/main/java/no/ntnu/gruppe53/service/LanguageOption.java
diff --git a/millions/src/main/java/no/ntnu/gruppe53/service/LanguageOption.java b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageOption.java
new file mode 100644
index 0000000..cd607b1
--- /dev/null
+++ b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageOption.java
@@ -0,0 +1,12 @@
+package no.ntnu.gruppe53.service;
+
+import java.util.Locale;
+
+/**
+ * Acts as a data-carrier representing a language with a string name and a {@link Locale}.
+ *
+ * @param name the name of the language
+ * @param locale the locale of the language
+ */
+public record LanguageOption(String name, Locale locale) {}
+
From 77a243bdee455198d2c72b21ea49ebf802c42471 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 20:53:39 +0200
Subject: [PATCH 09/24] Added LanguageRegistry
A registry of all possible languages that can be selected by LanguageManager.
---
.../gruppe53/service/LanguageRegistry.java | 54 +++++++++++++++++++
1 file changed, 54 insertions(+)
create mode 100644 millions/src/main/java/no/ntnu/gruppe53/service/LanguageRegistry.java
diff --git a/millions/src/main/java/no/ntnu/gruppe53/service/LanguageRegistry.java b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageRegistry.java
new file mode 100644
index 0000000..1122d2a
--- /dev/null
+++ b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageRegistry.java
@@ -0,0 +1,54 @@
+package no.ntnu.gruppe53.service;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Represents a registry of allowed {@link LanguageOption}s.
+ */
+public class LanguageRegistry {
+
+ /**
+ * Sets all possible languages that can be used.
+ */
+ private static final List languages = List.of(
+ new LanguageOption("English", new Locale("en")),
+ new LanguageOption("Norsk", new Locale("no"))
+ );
+
+ /**
+ * Sets the default language to be used.
+ * Searches through the registry to find english and sets it as default.
+ */
+ private static final LanguageOption defaultLang = languages.stream()
+ .filter(lang -> lang.locale().getLanguage().equals("en"))
+ .findFirst()
+ .orElse(languages.getFirst());
+
+ /**
+ * Returns all registered languages
+ *
+ * @return a list of registered languages
+ */
+ public static List getLanguages() {
+ return languages;
+ }
+
+ /**
+ * Returns the default language.
+ *
+ * @return the default language
+ */
+ public static LanguageOption getDefaultLanguage() {
+ return defaultLang;
+ }
+
+ /**
+ * Returns the default locale of the default language.
+ *
+ * @return the default locale
+ */
+ public static Locale getDefaultLocale() {
+ return defaultLang.locale();
+ }
+}
From 52ccd01b70b19215d0784245d059be753edf0963 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:14:11 +0200
Subject: [PATCH 10/24] Added LanguageRegistryTest
Test file for LanguageRegistry.
---
.../service/LanguageRegistryTest.java | 37 +++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 millions/src/test/java/no/ntnu/gruppe53/service/LanguageRegistryTest.java
diff --git a/millions/src/test/java/no/ntnu/gruppe53/service/LanguageRegistryTest.java b/millions/src/test/java/no/ntnu/gruppe53/service/LanguageRegistryTest.java
new file mode 100644
index 0000000..ab72057
--- /dev/null
+++ b/millions/src/test/java/no/ntnu/gruppe53/service/LanguageRegistryTest.java
@@ -0,0 +1,37 @@
+package no.ntnu.gruppe53.service;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Locale;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class LanguageRegistryTest {
+ @Test
+ void getLanguageShouldReturnCorrectAmountAndCorrectLanguages() {
+ var languages = LanguageRegistry.getLanguages();
+ boolean hasEnglish = languages.stream()
+ .anyMatch(lang -> lang.locale().equals(new Locale("en")));
+ boolean hasNorwegian = languages.stream()
+ .anyMatch(lang -> lang.locale().equals(new Locale("no")));
+
+ assertEquals(2, languages.size(), "Should be 2 languages.");
+ assertTrue(hasEnglish, "English should be a supported language.");
+ assertTrue(hasNorwegian, "Norwegian should be a supported language.");
+ }
+
+ @Test
+ void getDefaultLanguageShouldEqualEnglish() {
+ assertEquals("English", LanguageRegistry.getDefaultLanguage().name(),
+ "Default language name should be 'English'.");
+ assertEquals(new Locale("en"), LanguageRegistry.getDefaultLanguage().locale(),
+ "Default language locale should be 'en'.");
+ }
+
+ @Test
+ void getDefaultLocaleShouldReturnEnglish() {
+ assertEquals(new Locale("en"), LanguageRegistry.getDefaultLocale(),
+ "Default language locale should be 'en'.");
+ }
+
+}
\ No newline at end of file
From 492f7cf25eb267eb09c4c14e61b41720268e43a6 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:29:31 +0200
Subject: [PATCH 11/24] Updated StartView
Changed buttons and labels to use LanguageManager for string generation.
---
.../java/no/ntnu/gruppe53/view/StartView.java | 39 ++++++++++++++++---
1 file changed, 33 insertions(+), 6 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/StartView.java b/millions/src/main/java/no/ntnu/gruppe53/view/StartView.java
index 5756028..a49d52b 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/view/StartView.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/view/StartView.java
@@ -5,16 +5,23 @@
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
+
+import javafx.util.Subscription;
import no.ntnu.gruppe53.controller.StartViewController;
import no.ntnu.gruppe53.controller.ViewController;
+import no.ntnu.gruppe53.service.LanguageManager;
/**
* Represents the first page seen by the user of the application.
+ * Uses a {@link LanguageManager} for dynamically setting button and label text.
+ * Subscribes to changes in language set by the manager.
*/
public class StartView extends VBox {
private final Button newGameButton;
+ private final Button languageButton;
private final Button quitButton;
private final Image logo;
+ private Subscription languageSubscription;
/**
* Constructs the view and sets parameters to be exposed for the {@link StartViewController}.
@@ -22,6 +29,8 @@ public class StartView extends VBox {
* @param vm the {@link ViewController} for the view
*/
public StartView(ViewController vm) {
+ LanguageManager lm = LanguageManager.getInstance();
+
this.setAlignment(Pos.CENTER);
this.setSpacing(20);
@@ -32,29 +41,47 @@ public StartView(ViewController vm) {
logoView.setFitWidth(300);
logoView.setPreserveRatio(true);
- newGameButton = new Button("New Game");
- quitButton = new Button("Quit");
+ newGameButton = new Button();
+ newGameButton.textProperty().bind(lm.bindString("newGame"));
+
+ languageButton = new Button();
+ languageButton.setPrefWidth(200);
+ languageButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;");
+ languageSubscription = lm.getBundleProperty().subscribe(bundle -> {
+ languageButton.setText(lm.getString("languageButtonLabel"));
+ });
+
+ quitButton = new Button();
+ quitButton.textProperty().bind(lm.bindString("quitGame"));
newGameButton.setPrefWidth(200);
newGameButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;");
quitButton.setPrefWidth(200);
quitButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;");
- this.getChildren().addAll(logoView, newGameButton, quitButton);
+ this.getChildren().addAll(logoView, newGameButton, languageButton, quitButton);
}
/**
- * Sets the method to be run when
+ * Sets the method to be run when the new game button is pressed
*
- * @param action
+ * @param action the method to execute on button press
*/
public void setOnNewGame(Runnable action) {
newGameButton.setOnAction(e -> action.run());
}
/**
+ * Sets the method to be run when the languageButton is pressed
+ *
+ * @param action the method to execute on button press
+ */
+ public void setOnLanguage(Runnable action) {languageButton.setOnAction(e -> action.run());}
+
+ /**
+ * Sets the method to be run when the quit game button is pressed
*
- * @param action
+ * @param action the method to execute on button press
*/
public void setOnQuit(Runnable action) {
quitButton.setOnAction(e -> action.run());
From 0423e585ca5ce9f820ea2c477b0d1c3020bc3a18 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:33:13 +0200
Subject: [PATCH 12/24] Updated StartViewController
Added method to change language when clicking the language button in StartView. Updated JavaDoc.
---
.../controller/StartViewController.java | 28 ++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/controller/StartViewController.java b/millions/src/main/java/no/ntnu/gruppe53/controller/StartViewController.java
index de9e1ae..82d8fde 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/controller/StartViewController.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/controller/StartViewController.java
@@ -1,16 +1,23 @@
package no.ntnu.gruppe53.controller;
+import no.ntnu.gruppe53.service.LanguageManager;
+import no.ntnu.gruppe53.service.LanguageOption;
+import no.ntnu.gruppe53.service.LanguageRegistry;
import no.ntnu.gruppe53.view.StartView;
import javafx.application.Platform;
+import java.util.List;
+
/**
* The controller for {@link StartView},
* the first view for the player upon launching the app.
- * Contains two choices:
+ *
Contains three choices:
*
* - New Game
+ * - Language Select
* - Quit
*
+ * Uses a {@link LanguageManager} to change the UI language.
*/
public class StartViewController {
/**
@@ -25,6 +32,25 @@ public StartViewController(StartView view, ViewController vm) {
gameController.startNewGame();
});
+ view.setOnLanguage(() -> {
+ LanguageManager lm = LanguageManager.getInstance();
+ List availableLanguages = LanguageRegistry.getLanguages();
+
+ int currentIndex = 0;
+ for (int i = 0; i < availableLanguages.size(); i++) {
+ if (availableLanguages.get(i).locale().equals(lm.getLocale())) {
+ currentIndex = i;
+ break;
+ }
+ }
+
+ int nextIndex = (currentIndex + 1) % availableLanguages.size();
+ LanguageOption nextLanguage = availableLanguages.get(nextIndex);
+
+ lm.setLocale(nextLanguage.locale());
+ });
+
+
view.setOnQuit(Platform::exit);
}
}
\ No newline at end of file
From afea12c4e362d1992c52cf51660f295777a9cb37 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:35:18 +0200
Subject: [PATCH 13/24] Updated PlayerNameChooserView
Updated to use a LanguageManager to insert text in given language. Updated JavaDoc.
---
.../gruppe53/view/PlayerNameChooserView.java | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/PlayerNameChooserView.java b/millions/src/main/java/no/ntnu/gruppe53/view/PlayerNameChooserView.java
index 669c5f2..0711ae2 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/view/PlayerNameChooserView.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/view/PlayerNameChooserView.java
@@ -6,6 +6,7 @@
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
+import no.ntnu.gruppe53.service.LanguageManager;
import java.util.Optional;
@@ -19,18 +20,22 @@
* Represents a prompt for the {@link Player}'s name
*/
public class PlayerNameChooserView {
- Dialog dialog = new Dialog<>();
+ private final LanguageManager lm = LanguageManager.getInstance();
+ Dialog dialog;
/**
* Returns the dialog where the player can input a name of maximum 50 characters.
* A {@link TextFormatter} is used to set the maximum amount
* of characters for the player name.
+ * Uses a {@link LanguageManager} to determine text language.
*
* @return the dialog for player name input
*/
public Optional getDialog() {
+ dialog = new Dialog<>();
+
// Sets dialog title and header text
- dialog.setTitle("Player Name");
+ dialog.setTitle(null);
dialog.setHeaderText(null);
// Sets the dimensions for the dialog pane
@@ -41,8 +46,10 @@ public Optional getDialog() {
ButtonType confirmButton =
new ButtonType("Confirm", ButtonBar.ButtonData.OK_DONE);
-
dialog.getDialogPane().getButtonTypes().add(confirmButton);
+ Button actualConfirmButton = (Button) dialog.getDialogPane().lookupButton(confirmButton);
+
+ actualConfirmButton.textProperty().bind(lm.bindString("playerNameConfirmButton"));
TextField nameField = new TextField();
@@ -73,9 +80,8 @@ public Optional getDialog() {
counterLabel.setText(newVal.length() + " / 50");
});
- Label infoLabel = new Label(
- "Please enter your player name:"
- );
+ Label infoLabel = new Label();
+ infoLabel.textProperty().bind(lm.bindString("playerNameInfo"));
VBox layout = new VBox(15);
From 00d63a8e27471e3b52516726b5ccb2bb6c3b85c4 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:36:29 +0200
Subject: [PATCH 14/24] Updated CSVChooserView
Updated to use LanguageManager to set text to given language. Updated JavaDoc.
---
.../no/ntnu/gruppe53/view/CSVChooserView.java | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/CSVChooserView.java b/millions/src/main/java/no/ntnu/gruppe53/view/CSVChooserView.java
index 092e53f..a60d295 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/view/CSVChooserView.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/view/CSVChooserView.java
@@ -3,15 +3,18 @@
import javafx.scene.control.Alert;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
+import no.ntnu.gruppe53.service.LanguageManager;
import java.io.File;
/**
* Represents an {@link Alert} and {@link FileChooser} for informing user of .csv requirement and picking said file.
+ * Uses a {@link LanguageManager} to insert set text to target language.
*/
public class CSVChooserView {
private final FileChooser fileChooser = new FileChooser();
Alert newGameAlert = new Alert(Alert.AlertType.INFORMATION);
+ private final LanguageManager lm = LanguageManager.getInstance();
/**
* Returns the dialog for the alert and file chooser and exposes it to controllers.
@@ -21,13 +24,9 @@ public class CSVChooserView {
*/
public File getDialog(Stage stage) {
// Notifies the user about .csv selection
- newGameAlert.setTitle("Please read:");
- newGameAlert.setHeaderText("CSV File Required");
- newGameAlert.setContentText("Please select a .csv file of the stocks to be used for the exchange " +
- " in the next window.\n\nIf none is selected (or if .csv file is empty/malformed), a default file will be used." +
- "\n\nThe file needs to be formated as: StockTicker,CorporationName,PriceTypeDouble." +
- "\n\nExample: NVDA,Nvidia,191.27" + "\n\n'#' signifies comments, and along with lines not formatted " +
- "correctly, will be ignored.");
+ newGameAlert.setTitle(null);
+ newGameAlert.headerTextProperty().bind(lm.bindString("csvHeader"));
+ newGameAlert.contentTextProperty().bind(lm.bindString("csvContent"));
// Sets the dimensions of the alert to show all text, and as a band-aid, be resizable
newGameAlert.setResizable(true);
@@ -38,11 +37,12 @@ public File getDialog(Stage stage) {
newGameAlert.showAndWait();
// File chooser to choose .csv file
- fileChooser.setTitle("Select CSV File");
+ fileChooser.setTitle(lm.getString("csvFileChooserTitle"));
// Limits to .csv file only
+ fileChooser.getExtensionFilters().clear();
fileChooser.getExtensionFilters().add(
- new FileChooser.ExtensionFilter("CSV Files", "*.csv")
+ new FileChooser.ExtensionFilter(lm.getString("csvFileFilterLabel"), "*.csv")
);
return fileChooser.showOpenDialog(stage); }
From 00f6e802d52c145e2268b04a2edc9b371ebcee3a Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:38:48 +0200
Subject: [PATCH 15/24] Updated PlayerStartingMoneyChooserView
Updated to set text of UI using language manager. Updated JavaDoc.
---
.../view/PlayerStartingMoneyChooserView.java | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/PlayerStartingMoneyChooserView.java b/millions/src/main/java/no/ntnu/gruppe53/view/PlayerStartingMoneyChooserView.java
index 82b0c50..7dd6d65 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/view/PlayerStartingMoneyChooserView.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/view/PlayerStartingMoneyChooserView.java
@@ -5,15 +5,18 @@
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
+import no.ntnu.gruppe53.service.LanguageManager;
import java.math.BigDecimal;
import java.util.Optional;
/**
* Represents the prompt for the {@link Player}'s starting money.
+ * Uses a {@link LanguageManager} to set text to target language.
*/
public class PlayerStartingMoneyChooserView {
Dialog dialog = new Dialog<>();
+ private final LanguageManager lm = LanguageManager.getInstance();
/**
* Returns the dialog for the player's starting money.
@@ -25,7 +28,7 @@ public class PlayerStartingMoneyChooserView {
public Optional getDialog() {
// Sets dialog title and header text
- dialog.setTitle("Select starting money:");
+ dialog.setTitle(null);
dialog.setHeaderText(null);
// Sets the dimensions for the dialog pane
@@ -36,9 +39,12 @@ public Optional getDialog() {
ButtonType confirmButton =
new ButtonType("Confirm", ButtonBar.ButtonData.OK_DONE);
-
dialog.getDialogPane().getButtonTypes().add(confirmButton);
+ Button actualConfirmButton = (Button) dialog.getDialogPane().lookupButton(confirmButton);
+
+ actualConfirmButton.textProperty().bind(lm.bindString("playerStartingMoneyConfirmButton"));
+
Spinner spinner = new Spinner<>();
// Sets allowed values and step size for the spinner
@@ -78,9 +84,8 @@ public Optional getDialog() {
spinner.setPrefWidth(200);
- Label infoLabel = new Label(
- "Select your starting money balance for trading:"
- );
+ Label infoLabel = new Label();
+ infoLabel.textProperty().bind(lm.bindString("playerStartingMoneyInfoLabel"));
VBox layout = new VBox(15);
From 3126b4049e8b66ee750b486ba9494c2c19a412cf Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:42:50 +0200
Subject: [PATCH 16/24] Updated NavigationBar
Updated to use a LanguageManager to set text dynamically. Updated JavaDoc.
---
.../no/ntnu/gruppe53/view/NavigationBar.java | 34 +++++++++++++------
1 file changed, 23 insertions(+), 11 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/NavigationBar.java b/millions/src/main/java/no/ntnu/gruppe53/view/NavigationBar.java
index d6f7415..e494941 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/view/NavigationBar.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/view/NavigationBar.java
@@ -9,6 +9,7 @@
import javafx.scene.layout.Region;
import javafx.util.Subscription;
import no.ntnu.gruppe53.model.Player;
+import no.ntnu.gruppe53.service.LanguageManager;
import java.math.RoundingMode;
@@ -26,8 +27,10 @@
* clicking on the buttons representing the desired view.
* The {@link no.ntnu.gruppe53.controller.GameController} handles
* the actions for the buttons.
+ * Uses a {@link LanguageManager} to set text of UI elements dynamically.
*/
public class NavigationBar extends HBox {
+ private final LanguageManager lm = LanguageManager.getInstance();
private final Button marketButton;
private final Button portfolioButton;
@@ -45,7 +48,10 @@ public class NavigationBar extends HBox {
private Label netWorthLabel;
private Label moneyLabel;
-
+ /**
+ * Constructs the UI elements of the navigationBar.
+ * Binds the language manager to the textProperty of the UI elements to dynamically change the text.
+ */
public NavigationBar() {
this.setPadding(new Insets(15, 12, 15, 12));
@@ -56,11 +62,14 @@ public NavigationBar() {
"-fx-border-width: 0 0 5 0;");
- marketButton = new Button("Market");
+ marketButton = new Button();
+ marketButton.textProperty().bind(lm.bindString("marketButton"));
marketButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;");
- portfolioButton = new Button("Portfolio");
+ portfolioButton = new Button();
+ portfolioButton.textProperty().bind(lm.bindString("portfolioButton"));
portfolioButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;");
- historyButton = new Button("History");
+ historyButton = new Button();
+ historyButton.textProperty().bind(lm.bindString("historyButton"));
historyButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;");
Region spacer = new Region();
@@ -68,7 +77,8 @@ public NavigationBar() {
HBox playerBox = new HBox();
- Label playerLabelText = new Label("Player: ");
+ Label playerLabelText = new Label();
+ playerLabelText.textProperty().bind(lm.bindString("navPlayerLabelText"));
playerLabel = new Label("Player");
playerBox.setStyle("-fx-background-color: #ffe556;" +
@@ -85,7 +95,8 @@ public NavigationBar() {
HBox moneyBox = new HBox();
- Label moneyLabelText = new Label("Money: ");
+ Label moneyLabelText = new Label();
+ moneyLabelText.textProperty().bind(lm.bindString("navMoneyLabelText"));
moneyLabel = new Label();
moneyBox.setStyle("-fx-background-color: #ffe556;" +
@@ -100,7 +111,8 @@ public NavigationBar() {
HBox networthBox = new HBox();
- Label networthLabelText = new Label("Net Worth: ");
+ Label networthLabelText = new Label();
+ networthLabelText.textProperty().bind(lm.bindString("navNetWorthLabelText"));
netWorthLabel = new Label();
networthBox.setStyle("-fx-background-color: #ffe556;" +
@@ -161,16 +173,16 @@ public void bindPlayer(Player player) {
playerLabel.setText(player.getName());
netWorthSubscription = player.getNetWorthProperty().subscribe(newVal -> {
if (newVal != null) {
- netWorthLabel.setText("Net Worth: $" + newVal.setScale(2, RoundingMode.HALF_EVEN));
+ netWorthLabel.setText("$" + newVal.setScale(2, RoundingMode.HALF_EVEN));
} else {
- netWorthLabel.setText("Net Worth: $0.00");
+ netWorthLabel.setText("$0.00");
}
});
moneySubscription = player.getMoneyProperty().subscribe(newVal -> {
if (newVal != null) {
- moneyLabel.setText("Money: $" + newVal.setScale(2, RoundingMode.HALF_EVEN));
+ moneyLabel.setText("$" + newVal.setScale(2, RoundingMode.HALF_EVEN));
} else {
- moneyLabel.setText("Money: $0.00");
+ moneyLabel.setText("$0.00");
}
});
From 41ad7cbbb9300cfb16f04585379caef38f7a0f0e Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:47:26 +0200
Subject: [PATCH 17/24] Updated FooterBar
Updated to use LanguageManager to set text dynamically and uses a ComboBox to change language quickly. Updated JavaDoc.
---
.../java/no/ntnu/gruppe53/view/FooterBar.java | 171 +++++++++++++-----
1 file changed, 122 insertions(+), 49 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/FooterBar.java b/millions/src/main/java/no/ntnu/gruppe53/view/FooterBar.java
index efea8a3..58cf825 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/view/FooterBar.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/view/FooterBar.java
@@ -1,18 +1,25 @@
package no.ntnu.gruppe53.view;
-import java.math.RoundingMode;
+import java.util.Locale;
+import java.util.function.Consumer;
+
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
+import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
+import javafx.scene.control.ListCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.util.Subscription;
-import no.ntnu.gruppe53.model.Player;
+
import no.ntnu.gruppe53.controller.GameController;
import no.ntnu.gruppe53.model.Exchange;
+import no.ntnu.gruppe53.service.LanguageManager;
+import no.ntnu.gruppe53.service.LanguageOption;
+import no.ntnu.gruppe53.service.LanguageRegistry;
/*
Yellow Color, Hex: #ffe556 RGB(255, 229, 86)
@@ -23,65 +30,117 @@
/**
* A {@link HBox} view representing the footer/bottom to be used in a {@link BorderPane} layout.
+ * Uses a {@link LanguageManager} to change text to target language dynamically.
*/
public class FooterBar extends HBox {
private final Button advanceWeekButton;
- private Label currentWeekLabel;
- private Subscription currentWeekSubscription;
+ private Label currentWeekLabel;
+ private Subscription currentWeekSubscription;
- private Exchange exchange;
+ private Exchange exchange;
+ private final LanguageManager lm = LanguageManager.getInstance();
+
+ private final ComboBox languageBox;
/**
* Constructs the layout of the view and its components.
+ * Constructs a {@link ComboBox} to let the user switch language on the fly.
+ * Sets the text of the UI elements dynamically.
*/
public FooterBar() {
this.setSpacing(10);
this.setPadding(new Insets(15, 12, 15, 12));
- this.setStyle("-fx-background-color: #00bcf0;" +
- "-fx-border-color: #303539 transparent transparent transparent;" +
- "-fx-border-width: 5 0 0 0;");
-
-
-
-
- Region spacer = new Region();
- HBox.setHgrow(spacer, Priority.ALWAYS);
-
- HBox currentWeekBox = new HBox();
- currentWeekBox.setSpacing(10);
-
- currentWeekBox.setStyle("-fx-background-color: #ffe556;" +
- "-fx-text-fill: #303539;" +
- "-fx-background-radius: 8;" +
- "-fx-border-color: #303539;" +
- "-fx-border-radius: 8;"
- );
- currentWeekBox.setPadding(new Insets(8, 14, 8, 14));
- currentWeekBox.setAlignment(Pos.CENTER);
-
- currentWeekLabel = new Label();
- currentWeekBox.getChildren().add(currentWeekLabel);
-
-
-
- advanceWeekButton = new Button("Advance Week");
- advanceWeekButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;");
-
- this.getChildren().addAll(spacer, currentWeekBox, advanceWeekButton);
- }
-
- public void setExchange(Exchange exchange) {
- this.exchange = exchange;
- currentWeekSubscription = exchange.getCurrentWeekProperty().subscribe(newVal -> {
- if (exchange != null) {
- currentWeekLabel.setText("Current Week: " + newVal);
- } else {
- currentWeekLabel.setText("Current Week: None");
- }
- });
+ this.setStyle("-fx-background-color: #00bcf0;" +
+ "-fx-border-color: #303539 transparent transparent transparent;" +
+ "-fx-border-width: 5 0 0 0;");
+
+ languageBox = new ComboBox<>();
+ languageBox.getItems().addAll(LanguageRegistry.getLanguages());
+
+ Locale activeLocale = lm.getLocale();
+ LanguageOption activeOption = LanguageRegistry.getLanguages().stream()
+ .filter(lang -> lang.locale().equals(activeLocale))
+ .findFirst()
+ .orElse(LanguageRegistry.getDefaultLanguage());
+
+ languageBox.setValue(activeOption);
+
+ // Listens for changes in language from other sources (e.g. startView)
+ lm.getBundleProperty().subscribe(bundle -> {
+ java.util.Locale currentLocale = lm.getLocale();
+
+ LanguageOption matchingOption = LanguageRegistry.getLanguages().stream()
+ .filter(lang -> lang.locale().equals(currentLocale))
+ .findFirst()
+ .orElse(null);
+
+ if (matchingOption != null && !matchingOption.equals(languageBox.getValue())) {
+ languageBox.setValue(matchingOption);
+ }
+ });
+
+ languageBox.setCellFactory(list -> new ListCell<>() {
+ private Subscription cellSub;
+ @Override
+ protected void updateItem(LanguageOption item, boolean empty) {
+ super.updateItem(item, empty);
+ if (cellSub != null) cellSub.unsubscribe();
+
+ if (empty || item == null) {
+ setText(null);
+ } else {
+ cellSub = lm.getBundleProperty().subscribe(bundle -> setText(item.name()));
+ }
+ }
+ });
+
+ languageBox.setButtonCell(languageBox.getCellFactory().call(null));
+
+
+
+ Region spacer = new Region();
+ HBox.setHgrow(spacer, Priority.ALWAYS);
+
+ HBox currentWeekBox = new HBox();
+ currentWeekBox.setSpacing(10);
+
+ currentWeekBox.setStyle("-fx-background-color: #ffe556;" +
+ "-fx-text-fill: #303539;" +
+ "-fx-background-radius: 8;" +
+ "-fx-border-color: #303539;" +
+ "-fx-border-radius: 8;"
+ );
+ currentWeekBox.setPadding(new Insets(8, 14, 8, 14));
+ currentWeekBox.setAlignment(Pos.CENTER);
+
+ Label currentWeekLabelText = new Label();
+ currentWeekLabelText.textProperty().bind(lm.bindString("currentWeekLabelText"));
+ currentWeekLabel = new Label();
+ currentWeekBox.getChildren().addAll(currentWeekLabelText, currentWeekLabel);
+
+ advanceWeekButton = new Button();
+ advanceWeekButton.textProperty().bind(lm.bindString("advanceWeekButton"));
+ advanceWeekButton.setStyle("-fx-background-color: #ffe556; -fx-text-fill: #303539;");
+
+ this.getChildren().addAll(languageBox, spacer, currentWeekBox, advanceWeekButton);
+ }
- }
+ /**
+ * Sets the exchange and subscribes to week updates.
+ *
+ * @param exchange the {@link Exchange} used in the game
+ */
+ public void setExchange(Exchange exchange) {
+ this.exchange = exchange;
+ currentWeekSubscription = exchange.getCurrentWeekProperty().subscribe(newVal -> {
+ if (exchange != null) {
+ currentWeekLabel.setText(newVal.toString());
+ } else {
+ currentWeekLabel.setText("None");
+ }
+ });
+ }
/**
* Exposes the advanceWeekButton to {@link GameController} so it can
@@ -92,4 +151,18 @@ public void setExchange(Exchange exchange) {
public void onAdvanceButtonClick(Runnable action) {
advanceWeekButton.setOnAction(e -> action.run());
}
-}
+
+ /**
+ * Exoposes the LanguageOption ComboButton to the controller.
+ *
+ * @param action the action to be performed when selecting the language
+ */
+ public void onLanguageChange(Consumer action) {
+ languageBox.setOnAction(e -> {
+ LanguageOption selected = languageBox.getValue();
+ if (selected != null) {
+ action.accept(selected);
+ }
+ });
+ }
+}
\ No newline at end of file
From 6868f1ed5b29221ba611118b62410ab30e582d3e Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:47:42 +0200
Subject: [PATCH 18/24] Updated LanguageManager
Updated JavaDoc.
---
.../src/main/java/no/ntnu/gruppe53/service/LanguageManager.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java
index 09b9494..776f15a 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/service/LanguageManager.java
@@ -10,6 +10,7 @@
/**
* Manages the language used in the user-interface.
+ * The possible languages to be used are set by {@link LanguageRegistry}.
*/
public class LanguageManager {
From b3e25938e973a159a981f4551f4a3c6a484f30a7 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:54:56 +0200
Subject: [PATCH 19/24] Updated MarketView
Updated to use a LanguageManager for dynamically setting text to the target language. Updated JavaDoc. Added some comments.
---
.../no/ntnu/gruppe53/view/MarketView.java | 69 +++++++++++++------
1 file changed, 47 insertions(+), 22 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/MarketView.java b/millions/src/main/java/no/ntnu/gruppe53/view/MarketView.java
index 380ef7b..bab3197 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/view/MarketView.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/view/MarketView.java
@@ -16,6 +16,7 @@
import javafx.util.Subscription;
import no.ntnu.gruppe53.model.*;
import no.ntnu.gruppe53.service.FormatBigDecimal;
+import no.ntnu.gruppe53.service.LanguageManager;
/*
Yellow Color, Hex: #ffe556 RGB(255, 229, 86)
@@ -29,15 +30,14 @@
* price history statistics in the form of a {@link LineChart} through the
* {@link StockLineChartView} class, as well as buying {@link Stock}s.
* Extends borderpane to allow easy organizing of the internal layout.
+ * Uses a {@link LanguageManager} to set text to the targer language dynamically.
*/
public class MarketView extends BorderPane {
+ private final LanguageManager lm = LanguageManager.getInstance();
private Button purchaseButton;
private Label selectedStock;
-
-
-
//Dynamic Labels/Text
public TextField gross;
public TextField commission;
@@ -52,7 +52,6 @@ public class MarketView extends BorderPane {
private PurchaseCalculator purchaseCalculator;
private final StockLineChartView stockLineChartView;
- private Subscription playerSubscription;
private Subscription priceSubscription;
private Subscription grossSubscription;
private Subscription commissionSubscription;
@@ -71,6 +70,7 @@ public MarketView() {
/**
* Constructs the view and its components.
* Subscribes to a stock's sales price to update the value immediately.
+ * Subscribes to the current language to change text immediately.
* Uses a cell factory to format the string found in the stocksListView.
*
* @return the constructed MarketView
@@ -83,7 +83,8 @@ private VBox centerPane() {
//Blue Background color
vBox.setStyle("-fx-background-color: #00bcf0;");
- Label titleLabel = new Label("Stock Market");
+ Label titleLabel = new Label();
+ titleLabel.textProperty().bind(lm.bindString("marketTitle"));
titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; ");
VBox titleBox = new VBox(titleLabel);
@@ -97,14 +98,15 @@ private VBox centerPane() {
titleBox.setPadding(new Insets(4));
titleBox.setMaxWidth(200);
-
+ // Stock Graph
stockLineChartView.getChart().setMinHeight(0);
stockLineChartView.getChart().setMaxHeight(Double.MAX_VALUE);
stockLineChartView.getChart().setMinWidth(0);
stockLineChartView.getChart().setMaxWidth(Double.MAX_VALUE);
+ stockLineChartView.getChart().getXAxis().labelProperty().bind(lm.bindString("xAxisLabel"));
+ stockLineChartView.getChart().getYAxis().labelProperty().bind(lm.bindString("yAxisLabel"));
-
-
+ // List of stocks
stocksListView = new ListView<>();
stocksListView.setMinHeight(0);
stocksListView.setMaxHeight(Double.MAX_VALUE);
@@ -114,6 +116,7 @@ private VBox centerPane() {
stocksListView.setCellFactory(list -> new ListCell<>() {
private Subscription cellSubscription;
+ private Subscription langSubscription;
@Override
protected void updateItem(Stock stock, boolean empty) {
@@ -123,22 +126,37 @@ protected void updateItem(Stock stock, boolean empty) {
cellSubscription.unsubscribe();
cellSubscription = null;
}
+ if (langSubscription != null) {
+ langSubscription.unsubscribe();
+ langSubscription = null;
+ }
if (empty || stock == null) {
setText(null);
} else {
- cellSubscription = stock.salesPriceProperty().subscribe(newPrice -> {
- setText(stock.getSymbol() + " - " + stock.getCompany() + " | Price: $" +
- FormatBigDecimal.formatNumber(newPrice)
- + " | Price change: " + FormatBigDecimal.formatNumber(stock.getLatestPriceChange())
- + " | Highest price: " + FormatBigDecimal.formatNumber(stock.getHighestPrice())
- + " | Lowest price: " + FormatBigDecimal.formatNumber(stock.getLowestPrice()));
- });
+ Runnable updateTextAction = () -> {
+ String template = lm.getString("stockCellFormat");
+ setText(String.format(template,
+ stock.getSymbol(),
+ stock.getCompany(),
+ FormatBigDecimal.formatNumber(stock.salesPriceProperty().get()),
+ FormatBigDecimal.formatNumber(stock.getLatestPriceChange()),
+ FormatBigDecimal.formatNumber(stock.getHighestPrice()),
+ FormatBigDecimal.formatNumber(stock.getLowestPrice())
+ ));
+ };
+
+ cellSubscription = stock.salesPriceProperty().subscribe(newPrice -> updateTextAction.run());
+ langSubscription = lm.getBundleProperty().subscribe(bundle -> updateTextAction.run());
+
+ updateTextAction.run();
}
}
});
- HBox stockListBox = new HBox(stocksListView);
+
+ // Container for list of stocks
+ HBox stockListBox = new HBox(stocksListView);
stockListBox.setStyle("-fx-background-color: #ffe556;" +
"-fx-text-fill: #303539;" +
"-fx-background-radius: 8;" +
@@ -146,6 +164,7 @@ protected void updateItem(Stock stock, boolean empty) {
"-fx-border-radius: 8;"
);
+ // Container for graph of stocks
HBox stockChartBox = new HBox(stockLineChartView.getChart());
stockChartBox.setStyle("-fx-background-color: #ffe556;" +
"-fx-text-fill: #303539;" +
@@ -176,14 +195,16 @@ protected void updateItem(Stock stock, boolean empty) {
HBox selectedStockBox = new HBox();
- Label selectedStockLabel = new Label("Selected Stock: ");
+ Label selectedStockLabel = new Label();
+ selectedStockLabel.textProperty().bind(lm.bindString("selectedStock"));
selectedStock = new Label("");
selectedStockBox.getChildren().addAll(selectedStockLabel, selectedStock);
HBox quantityBox = new HBox();
- Label quantityLabel = new Label("Quantity: ");
+ Label quantityLabel = new Label();
+ quantityLabel.textProperty().bind(lm.bindString("purchaseQuantity"));
quantitySpinner = new Spinner<>(1, 1_000_000, 1);
quantitySpinner.setEditable(true);
@@ -191,11 +212,13 @@ protected void updateItem(Stock stock, boolean empty) {
HBox calculationBox = new HBox();
- Label grossLabel = new Label("Gross: ");
+ Label grossLabel = new Label();
+ grossLabel.textProperty().bind(lm.bindString("purchaseGross"));
gross = new TextField();
gross.setEditable(false);
gross.setMaxWidth(200);
- Label commissionLabel = new Label("Commission: ");
+ Label commissionLabel = new Label();
+ commissionLabel.textProperty().bind(lm.bindString("purchaseCommission"));
commission = new TextField();
commission.setEditable(false);
commission.setMaxWidth(200);
@@ -204,14 +227,16 @@ protected void updateItem(Stock stock, boolean empty) {
HBox totalBox = new HBox();
- Label totalLabel = new Label("Total: ");
+ Label totalLabel = new Label();
+ totalLabel.textProperty().bind(lm.bindString("purchaseTotal"));
total = new TextField("");
total.setEditable(false);
total.setMaxWidth(200);
totalBox.getChildren().addAll(totalLabel, total);
- purchaseButton = new Button("Purchase");
+ purchaseButton = new Button();
+ purchaseButton.textProperty().bind(lm.bindString("purchaseButton"));
VBox purchaseBox = new VBox(5);
From 331b8b13ac0981453ee45ef673141985fbb6a1e3 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 22:58:59 +0200
Subject: [PATCH 20/24] Updated GameController
Updated to include method in footer for setting language locale. Updated JavaDoc.
---
.../ntnu/gruppe53/controller/GameController.java | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java b/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java
index 6bc40d8..ae36e05 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java
@@ -1,25 +1,24 @@
package no.ntnu.gruppe53.controller;
import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
import javafx.scene.control.Alert;
import javafx.stage.Stage;
import no.ntnu.gruppe53.model.*;
+import no.ntnu.gruppe53.service.LanguageManager;
import no.ntnu.gruppe53.service.StockLineChartService;
import no.ntnu.gruppe53.view.*;
/**
* Represents the main controller for the views of the stock game.
+ * Uses a {@link LanguageManager} for methods to change text dynamically.
*/
public class GameController {
private Player player;
private Exchange exchange;
private List stocks;
+ private final LanguageManager lm = LanguageManager.getInstance();
private final ViewController vm;
@@ -111,6 +110,7 @@ public void showTransactionHistory() {
* Initializes the {@link NavigationBar} and {@link FooterBar} for the controller.
* Binds functionality to exposed buttons and lists in views.
* Sets subscriptions for observable values for views.
+ * Sets the method for setting the selected language locale for the footer.
* @param nav the NavigationBar view
* @param footer the FooterBar view
*/
@@ -119,6 +119,10 @@ public void initialize(NavigationBar nav, FooterBar footer) {
nav.onPortfolioButtonClick(() -> vm.switchView("portfolio"));
nav.onHistoryButtonClick(() -> vm.switchView("history"));
+ footer.onLanguageChange(selected -> {
+ lm.setLocale(selected.locale());
+ });
+
footer.onAdvanceButtonClick(() -> {
if (exchange != null) {
exchange.advance();
@@ -223,8 +227,8 @@ private PurchaseCalculator calculatePurchase(Stock stock, int quantity) {
/**
* Creates a {@link SaleCalculator} that can be used for the
- * for the setSaleCalculator() function in the {@link PortfolioView}.
- * @param share
+ * setSaleCalculator() function in the {@link PortfolioView}.
+ * @param share the share to use the calculator on
* @return {@link SaleCalculator}
*/
private SaleCalculator calculateSale(Share share) {
From 7fc4129e77c90d4e23017cdd6e505cd88cc48e61 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 23:13:24 +0200
Subject: [PATCH 21/24] Updated PortfolioView
Updated UI to use language manager to dynamically set text. Updated JavaDoc.
---
.../no/ntnu/gruppe53/view/PortfolioView.java | 65 ++++++++++++++-----
1 file changed, 47 insertions(+), 18 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/PortfolioView.java b/millions/src/main/java/no/ntnu/gruppe53/view/PortfolioView.java
index 7388c16..290d375 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/view/PortfolioView.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/view/PortfolioView.java
@@ -2,7 +2,6 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
-import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javafx.collections.ObservableList;
@@ -23,8 +22,8 @@
import no.ntnu.gruppe53.model.Player;
import no.ntnu.gruppe53.model.SaleCalculator;
import no.ntnu.gruppe53.model.Share;
-import no.ntnu.gruppe53.model.Stock;
import no.ntnu.gruppe53.service.FormatBigDecimal;
+import no.ntnu.gruppe53.service.LanguageManager;
/*
Yellow Color, Hex: #ffe556 RGB(255, 229, 86)
@@ -40,13 +39,12 @@
* and the ability to sell the share for money.
* Extends the {@link BorderPane}
for easy layout setup.
* Events are handled by the {@link no.ntnu.gruppe53.controller.GameController}.
+ * Uses a {@link LanguageManager} to dynamically set text based on target language.
*/
public class PortfolioView extends BorderPane {
- private Button sellButton;
+ private final LanguageManager lm = LanguageManager.getInstance();
- //NavigationBar Buttons
- private Button marketButton;
- private Button portfolioButton;
+ private Button sellButton;
//Static Labels
private Label titleLabel;
@@ -79,7 +77,8 @@ public PortfolioView() {
/**
* Constructs the view of the portfolio.
* Subscribes to shares for sales price updates.
- * Uses a cell factory to format the displayed string representing each share.
+ * Uses a cell factory to format the displayed string representing each share.
+ * The sell factory has the text dynamically set by the language manager.
*
* @return a VBox of the portfolio view
*/
@@ -90,7 +89,8 @@ private VBox centerPane() {
//Blue Background color
vBox.setStyle("-fx-background-color: #00bcf0; ");
- titleLabel = new Label("Portfolio");
+ titleLabel = new Label();
+ titleLabel.textProperty().bind(lm.bindString("portfolioTitle"));
titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; -fx-text-fill: #303539;");
VBox titleBox = new VBox(titleLabel);
@@ -110,6 +110,7 @@ private VBox centerPane() {
portfolioListView.setCellFactory(list -> new ListCell<>() {
private Subscription cellSubscription;
+ private Subscription langSubscription;
@Override
protected void updateItem(Share item, boolean empty) {
@@ -119,15 +120,32 @@ protected void updateItem(Share item, boolean empty) {
cellSubscription.unsubscribe();
cellSubscription = null;
}
+ if (langSubscription != null) {
+ langSubscription.unsubscribe();
+ langSubscription = null;
+ }
if (empty || item == null || item.getStock() == null) {
setText(null);
setGraphic(null);
} else {
setText(null);
- cellSubscription = item.getStock().salesPriceProperty().subscribe(currentPrice -> {
+
+
+ Runnable updateGraphicAction = () -> {
+ BigDecimal currentPrice = item.getStock().salesPriceProperty().get();
setGraphic(createColoredShareText(item, currentPrice));
+ };
+
+ cellSubscription = item.getStock().salesPriceProperty().subscribe(currentPrice -> {
+ updateGraphicAction.run();
});
+
+ langSubscription = lm.getBundleProperty().subscribe(bundle -> {
+ updateGraphicAction.run();
+ });
+
+ updateGraphicAction.run();
}
}
});
@@ -150,22 +168,26 @@ protected void updateItem(Share item, boolean empty) {
HBox selectedShareBox = new HBox();
- Label selectedShareLabel = new Label("Selected Share: ");
+ Label selectedShareLabel = new Label();
+ selectedShareLabel.textProperty().bind(lm.bindString("selectedShare"));
selectedShare = new Label();
selectedShareBox.getChildren().addAll(selectedShareLabel, selectedShare);
HBox calculationBox = new HBox();
- Label grossLabel = new Label("Gross: ");
+ Label grossLabel = new Label();
+ grossLabel.textProperty().bind(lm.bindString("portfolioGross"));
gross = new TextField();
gross.setEditable(false);
gross.setMaxWidth(200);
- Label commissionLabel = new Label("Commission: ");
+ Label commissionLabel = new Label();
+ commissionLabel.textProperty().bind(lm.bindString("portfolioCommission"));
commission = new TextField();
commission.setEditable(false);
commission.setMaxWidth(200);
- Label taxLabel = new Label("Tax: ");
+ Label taxLabel = new Label();
+ taxLabel.textProperty().bind(lm.bindString("portfolioTax"));
tax = new TextField();
tax.setEditable(false);
tax.setMaxWidth(200);
@@ -176,14 +198,16 @@ protected void updateItem(Share item, boolean empty) {
HBox totalBox = new HBox();
- Label totalLabel = new Label("Total: ");
+ Label totalLabel = new Label();
+ totalLabel.textProperty().bind(lm.bindString("portfolioTotal"));
total = new TextField();
total.setEditable(false);
total.setMaxWidth(200);
totalBox.getChildren().addAll(totalLabel, total);
- sellButton = new Button("Sell");
+ sellButton = new Button();
+ sellButton.textProperty().bind(lm.bindString("sellButton"));
VBox saleBox = new VBox(5);
@@ -316,11 +340,16 @@ private TextFlow createColoredShareText(Share share, BigDecimal currentPrice) {
String text = share.getStock().getSymbol()
+ " - "
+ share.getStock().getCompany()
- + " | Qty: "
+ + " | "
+ + lm.getString("portfolioShareQuantity")
+ share.getQuantity()
- + " | Buy price: $"
+ + " | "
+ + lm.getString("portfolioBuyPrice")
+ + "$"
+ FormatBigDecimal.formatNumber(share.getPurchasePrice())
- + " | Current Price: $";
+ + " | "
+ + lm.getString("portfolioCurrentPrice")
+ + "$";
Text t1 = new Text(text);
Text tValue = new Text(FormatBigDecimal.formatNumber(currentPrice));
From 547c9f5e83834e888806cd2890641087375834a4 Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 23:14:59 +0200
Subject: [PATCH 22/24] Updated TransactionArchiveView
Updated to use language manager to set text dynamically. Updated JavaDoc.
---
.../gruppe53/view/TransactionArchiveView.java | 59 ++++++++++++++-----
1 file changed, 44 insertions(+), 15 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/view/TransactionArchiveView.java b/millions/src/main/java/no/ntnu/gruppe53/view/TransactionArchiveView.java
index 630adc8..fb5e11b 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/view/TransactionArchiveView.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/view/TransactionArchiveView.java
@@ -10,11 +10,14 @@
import javafx.scene.layout.VBox;
import java.math.BigDecimal;
import java.math.RoundingMode;
+
+import javafx.util.Subscription;
import no.ntnu.gruppe53.model.Player;
import no.ntnu.gruppe53.model.Transaction;
import no.ntnu.gruppe53.model.Purchase;
import no.ntnu.gruppe53.model.Sale;
import no.ntnu.gruppe53.service.FormatBigDecimal;
+import no.ntnu.gruppe53.service.LanguageManager;
/*
Yellow Color, Hex: #ffe556 RGB(255, 229, 86)
@@ -28,6 +31,7 @@
* Extends the {@link BorderPane} for easy layout setup.
*/
public class TransactionArchiveView extends BorderPane {
+ private final LanguageManager lm = LanguageManager.getInstance();
private ListView historyListView;
private Label titleLabel;
@@ -46,6 +50,7 @@ public TransactionArchiveView() {
* Assigns red text to purchases, and green text to sales.
* Uses the formatBigDecimal method to convert the BigDecimal to a
* string with 2 decimals.
+ * Uses a {@link LanguageManager} to dynamically set text based on target language.
*
* @return a VBox of the constructed view of the transaction archive
*/
@@ -54,7 +59,8 @@ private VBox centerPane() {
vBox.setPadding(new Insets(15, 12, 15, 12));
vBox.setStyle("-fx-background-color: #00bcf0; ");
- titleLabel = new Label("Transaction History");
+ titleLabel = new Label();
+ titleLabel.textProperty().bind(lm.bindString("historyTitle"));
titleLabel.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; -fx-text-fill: #303539;");
VBox titleBox = new VBox(titleLabel);
@@ -73,28 +79,50 @@ private VBox centerPane() {
historyListView.setMaxHeight(Double.MAX_VALUE);
historyListView.setCellFactory(list -> new ListCell<>() {
+ private Subscription langSubscription;
+
@Override
protected void updateItem(Transaction item, boolean empty) {
super.updateItem(item, empty);
+ if (langSubscription != null) {
+ langSubscription.unsubscribe();
+ langSubscription = null;
+ }
+
if (empty || item == null) {
setText(null);
setStyle("");
} else {
- String type = (item instanceof Purchase) ? "PURCHASE" : "SALE";
- String symbol = item.getShare().getStock().getSymbol();
- BigDecimal quantity = item.getShare().getQuantity();
- BigDecimal total = item.getCalculator().calculateTotal();
-
- String cellText = String.format("Week %d | %s | %s | Qty: %s | Total: $%s",
- item.getWeek(),
- type,
- symbol,
- FormatBigDecimal.formatNumber(quantity),
- FormatBigDecimal.formatNumber(total)
- );
-
- setText(cellText);
+ Runnable updateTextAction = () -> {
+ String typeStr;
+ // Changed to allow for more transaction types in the future
+ if (item instanceof Purchase) {
+ typeStr = lm.getString("historyTypePurchase");
+ } else if (item instanceof Sale) {
+ typeStr = lm.getString("historyTypeSale");
+ } else {
+ typeStr = "ERROR";
+ }
+
+ BigDecimal quantity = item.getShare().getQuantity();
+ BigDecimal total = item.getCalculator().calculateTotal();
+ String symbol = item.getShare().getStock().getSymbol();
+
+ String template = lm.getString("historyCellFormat");
+
+ setText(String.format(template,
+ item.getWeek(),
+ typeStr,
+ symbol,
+ FormatBigDecimal.formatNumber(quantity),
+ FormatBigDecimal.formatNumber(total)
+ ));
+ };
+
+ langSubscription = lm.getBundleProperty().subscribe(bundle -> updateTextAction.run());
+
+ updateTextAction.run();
if (item instanceof Purchase) {
setStyle("-fx-text-fill: #c0392b; -fx-font-weight: bold;");
@@ -105,6 +133,7 @@ protected void updateItem(Transaction item, boolean empty) {
}
});
+
VBox historyListBox = new VBox(historyListView);
historyListBox.setStyle("-fx-background-color: #ffe556;" +
"-fx-text-fill: #303539;" +
From a25a9966d75a9a1be82dcfd575a4460d552ee46f Mon Sep 17 00:00:00 2001
From: Roar
Date: Sun, 24 May 2026 23:50:30 +0200
Subject: [PATCH 23/24] Updated GameController
Added two new subscriptions: selectedStockPriceSubscription and selectedSharePriceSubscription to dynamically update the calculator fields on stock/share price change.
---
.../gruppe53/controller/GameController.java | 38 +++++++++++++++++--
1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java b/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java
index ae36e05..1a1d865 100644
--- a/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java
+++ b/millions/src/main/java/no/ntnu/gruppe53/controller/GameController.java
@@ -5,6 +5,7 @@
import javafx.scene.control.Alert;
import javafx.stage.Stage;
+import javafx.util.Subscription;
import no.ntnu.gruppe53.model.*;
import no.ntnu.gruppe53.service.LanguageManager;
import no.ntnu.gruppe53.service.StockLineChartService;
@@ -28,6 +29,9 @@ public class GameController {
private PurchaseCalculator purchaseCalculator;
private SaleCalculator saleCalculator;
+ private Subscription selectedStockPriceSubscription;
+ private Subscription selectedSharePriceSubscription;
+
/**
* Initializes the {@link ViewController} to be used for switching views.
* @param vm {@code ViewController} to be used when switching views
@@ -130,6 +134,11 @@ public void initialize(NavigationBar nav, FooterBar footer) {
});
marketView.onStockSelection(stock -> {
+ if (selectedStockPriceSubscription != null) {
+ selectedStockPriceSubscription.unsubscribe();
+ selectedStockPriceSubscription = null;
+ }
+
if (stock != null) {
marketView.setSelectedStockLabel(stock.getSymbol() + " - " + stock.getCompany());
marketView.setupPriceSubscription(stock, currentStock -> {
@@ -138,6 +147,16 @@ public void initialize(NavigationBar nav, FooterBar footer) {
});
marketView.setPurchaseCalculator(calculatePurchase(marketView.getSelectedStock(),
marketView.getQuantityPurchase()));
+
+ selectedStockPriceSubscription = stock.salesPriceProperty().subscribe(newPrice -> {
+ if (marketView.getSelectedStock() != null) {
+ marketView.setPurchaseCalculator(calculatePurchase(
+ marketView.getSelectedStock(),
+ marketView.getQuantityPurchase()
+ ));
+ }
+ });
+
} else {
marketView.setSelectedStockLabel("");
marketView.clearChart();
@@ -149,17 +168,28 @@ public void initialize(NavigationBar nav, FooterBar footer) {
if (stock != null) purchaseStock(stock.getSymbol(), marketView.getQuantityPurchase());
});
- marketView.onQuantitySelect(() -> {
- if (marketView.getSelectedStock() != null) {
+ marketView.onQuantitySelect(() -> {
+ if (marketView.getSelectedStock() != null) {
marketView.setPurchaseCalculator(calculatePurchase(marketView.getSelectedStock(),
marketView.getQuantityPurchase()));
}
- });
+ });
portfolioView.onShareSelection(share -> {
+ if (selectedSharePriceSubscription != null) {
+ selectedSharePriceSubscription.unsubscribe();
+ selectedSharePriceSubscription = null;
+ }
if (share != null) {
portfolioView.setSelectedShareLabel(share.getStock().getSymbol() + " - " + share.getStock().getCompany());
portfolioView.setSaleCalculator(calculateSale(portfolioView.getSelectedShare()));
+
+ selectedSharePriceSubscription = share.getStock().salesPriceProperty().subscribe(newPrice -> {
+ if (portfolioView.getSelectedShare() != null) {
+ portfolioView.setSaleCalculator(calculateSale(portfolioView.getSelectedShare()));
+ }
+ });
+
} else {
portfolioView.setSelectedShareLabel("");
}
@@ -209,7 +239,7 @@ private void sellShare(Share selectedShare) {
/**
* Creates a {@link PurchaseCalculator} that can be used for the
- * for the setPurchaseCalculator() function in the {@link MarketView}.
+ * setPurchaseCalculator() function in the {@link MarketView}.
* @param stock the currently selected stock
* @param quantity the currently selected quantity
* @return {@link PurchaseCalculator}
From d94df4e5b1d134bb0ace8a402da007725faef451 Mon Sep 17 00:00:00 2001
From: Roar
Date: Mon, 25 May 2026 12:33:37 +0200
Subject: [PATCH 24/24] Updated pom.xml
Changed org.openjfx javafx-controls to the required 25.0.1 version.
---
millions/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/millions/pom.xml b/millions/pom.xml
index fed2ae7..117b7d9 100644
--- a/millions/pom.xml
+++ b/millions/pom.xml
@@ -26,7 +26,7 @@
org.openjfx
javafx-controls
- 25.0.3
+ 25.0.1