diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/appcontrollers/SettingsAppController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/appcontrollers/SettingsAppController.java index 35b8555..8a00114 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/appcontrollers/SettingsAppController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/appcontrollers/SettingsAppController.java @@ -44,10 +44,12 @@ public class SettingsAppController implements AppController { * Constructs the Settings controller and initializes UI bindings, * file validation, and event wiring. * - * @param settingsApp the settings UI view + * @param settingsApp the settings UI view * @param filePickerApp the file picker UI view */ - public SettingsAppController(final SettingsApp settingsApp, final FilePickerApp filePickerApp) { + public SettingsAppController( + final SettingsApp settingsApp, + final FilePickerApp filePickerApp) { this.settingsApp = settingsApp; this.filePickerApp = filePickerApp; @@ -73,44 +75,55 @@ public SettingsAppController(final SettingsApp settingsApp, final FilePickerApp } private void wireControls() { - settingsApp.getMasterVolumeSlider().valueProperty().addListener((obs, ov, nv) -> - AudioManager.get().setMasterVolume(nv.doubleValue() / 100.0)); + wireVolumeControls(); + if (!settingsApp.isLoggedIn()) { + wireLoggedOutControls(); + } else { + wireLoggedInControls(); + } + } - settingsApp.getSfxVolumeSlider().valueProperty().addListener((obs, ov, nv) -> - AudioManager.get().setSfxVolume(nv.doubleValue() / 100.0)); + private void wireVolumeControls() { + settingsApp.getMasterVolumeSlider().valueProperty().addListener( + (obs, ov, nv) -> AudioManager.get().setMasterVolume(nv.doubleValue() / 100.0)); + settingsApp.getSfxVolumeSlider().valueProperty().addListener( + (obs, ov, nv) -> AudioManager.get().setSfxVolume(nv.doubleValue() / 100.0)); + settingsApp.getMasterVolumeSlider() + .setValue(AudioManager.get().getMasterVolume() * 100.0); + settingsApp.getSfxVolumeSlider() + .setValue(AudioManager.get().getSfxVolume() * 100.0); + } - settingsApp.getMasterVolumeSlider().setValue(AudioManager.get().getMasterVolume() * 100.0); - settingsApp.getSfxVolumeSlider().setValue(AudioManager.get().getSfxVolume() * 100.0); + private void wireLoggedOutControls() { + settingsApp.getOpenFileChooserButton().setOnAction(event -> openFilePicker()); + } - if (!settingsApp.isLoggedIn()) { - settingsApp.getOpenFileChooserButton().setOnAction(event -> openFilePicker()); + private void wireLoggedInControls() { + settingsApp.getApplyGradientButton().setOnAction(event -> { + if (onGradientChanged != null) { + onGradientChanged.accept(settingsApp.getGradientConfig()); + } + }); - } else { - settingsApp.getApplyGradientButton().setOnAction(event -> { - if (onGradientChanged != null) { - onGradientChanged.accept(settingsApp.getGradientConfig()); - } - }); + settingsApp.getApplyNameButton().setOnAction(event -> { + final String name = settingsApp.getPlayerNameField().getText().trim(); + if (!name.isEmpty() && onPlayerNameChanged != null) { + onPlayerNameChanged.accept(name); + settingsApp.getPlayerNameField().clear(); + } + }); - settingsApp.getApplyNameButton().setOnAction(event -> { - String name = settingsApp.getPlayerNameField().getText().trim(); - if (!name.isEmpty() && onPlayerNameChanged != null) { - onPlayerNameChanged.accept(name); - settingsApp.getPlayerNameField().clear(); - } - }); + settingsApp.getLogoutButton().setOnAction(event -> { + if (onLogout != null) { + onLogout.run(); + } + }); - settingsApp.getLogoutButton().setOnAction(event -> { - if (onLogout != null) { - onLogout.run(); - } - }); - settingsApp.getPowerOffButton().setOnAction(event -> { - if (onPowerOff != null) { - onPowerOff.run(); - } - }); - } + settingsApp.getPowerOffButton().setOnAction(event -> { + if (onPowerOff != null) { + onPowerOff.run(); + } + }); } /** @@ -125,7 +138,7 @@ public void setLoggedIn(final boolean status) { private void openFilePicker() { if (filePickerApp.getRoot().getParent() == null) { - var parent = settingsApp.getRoot().getParent(); + final var parent = settingsApp.getRoot().getParent(); if (parent instanceof Pane pane) { pane.getChildren().add(filePickerApp.getRoot()); filePickerApp.centerInParent(); @@ -159,26 +172,56 @@ private void refreshView() { settingsApp.updateContent(); } + /** + * Sets the callback invoked when the user successfully selects a valid stock file path. + * + * @param callback the runnable to invoke after a successful path selection + */ public void setOnUserPathSelected(final Runnable callback) { onUserSelection = callback; } + /** + * Sets the callback invoked when the selected stock file fails validation. + * + * @param callback the runnable to invoke when file validation fails + */ public void setOnSelectedFileFailed(final Runnable callback) { onSelectedFileFailed = callback; } + /** + * Sets the callback invoked when the user triggers a logout action. + * + * @param callback the runnable to invoke on logout + */ public void setOnLogout(final Runnable callback) { onLogout = callback; } + /** + * Sets the callback invoked when the user triggers the power-off action. + * + * @param callback the runnable to invoke on power-off + */ public void setOnPowerOff(final Runnable callback) { onPowerOff = callback; } + /** + * Sets the callback invoked when the user applies a new player name. + * + * @param callback the consumer to invoke with the newly entered player name + */ public void setOnPlayerNameChanged(final Consumer callback) { onPlayerNameChanged = callback; } + /** + * Sets the callback invoked when the user applies a new gradient configuration. + * + * @param callback the consumer to invoke with the selected gradient config + */ public void setOnGradientChanged(final Consumer callback) { onGradientChanged = callback; } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/viewcontrollers/DesktopViewController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/viewcontrollers/DesktopViewController.java index 7e340f4..256169a 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/viewcontrollers/DesktopViewController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/controller/viewcontrollers/DesktopViewController.java @@ -146,7 +146,6 @@ private List initializeApps(GameController gameController) { SettingsAppController settingsAppController = new SettingsAppController(settingsApp, filePickerApp); settingsAppController.setLoggedIn(true); - settingsAppController.setOnGradientChanged(cfg -> desktopView.getDesktopPane().setStyle( SettingsApp.buildCssGradient(cfg.colorA(), cfg.colorB(), cfg.direction()))); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/SettingsApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/SettingsApp.java index 22ef4e4..2f5b4e1 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/SettingsApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/view/apps/SettingsApp.java @@ -18,6 +18,7 @@ import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; +import javafx.stage.FileChooser; /** * Settings app: allows the user to change audio volume, appearance, and profile. @@ -28,7 +29,6 @@ public class SettingsApp extends Popup { public record GradientConfig(Color colorA, Color colorB, String direction) {} private final Button openFileChooserButton; - private final javafx.stage.FileChooser fileChooser; private final Slider masterVolumeSlider; private final Slider sfxVolumeSlider; @@ -54,13 +54,13 @@ public SettingsApp(final boolean isLoggedIn) { super(400, 500, 80, 400, App.SETTINGS); this.isLoggedIn = isLoggedIn; - openFileChooserButton = new Button("Import stock file…"); + openFileChooserButton = new Button("Import stock file"); openFileChooserButton.getStyleClass().add("secondary-button"); - fileChooser = new javafx.stage.FileChooser(); + FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Select .csv stock file"); fileChooser.getExtensionFilters().add( - new javafx.stage.FileChooser.ExtensionFilter("CSV files", "*.csv")); + new FileChooser.ExtensionFilter("CSV files", "*.csv")); masterVolumeSlider = createSlider(); sfxVolumeSlider = createSlider(); @@ -69,38 +69,38 @@ public SettingsApp(final boolean isLoggedIn) { gradientColorB = new ColorPicker(Color.web("#1a2a3a")); gradientDirection = new ComboBox<>(); gradientDirection.getItems().addAll( - "Top → Bottom", - "Bottom → Top", - "Left → Right", - "Right → Left", - "Diagonal ↘", - "Diagonal ↙" + "Top to Bottom", + "Bottom to Top", + "Left to Right", + "Right to Left", + "Top Left to Bottom Right", + "Top Right to Bottom Left" ); - gradientDirection.setValue("Top → Bottom"); + gradientDirection.setValue("Top to Bottom"); gradientDirection.getStyleClass().add("settings-combo"); applyGradientButton = new Button("Apply"); applyGradientButton.getStyleClass().add("primary-button"); playerNameField = new TextField(); - playerNameField.setPromptText("Enter new name…"); + playerNameField.setPromptText("Enter new name"); playerNameField.getStyleClass().add("settings-text-field"); applyNameButton = new Button("Apply"); applyNameButton.getStyleClass().add("primary-button"); + applyNameButton.setMinWidth(Region.USE_PREF_SIZE); - logoutButton = new Button("⇤ Log out"); + logoutButton = new Button("Log out"); logoutButton.getStyleClass().add("settings-logout-button"); logoutButton.setMaxWidth(Double.MAX_VALUE); - powerOffButton = new Button("⏻ Power off"); + powerOffButton = new Button("Power off"); powerOffButton.getStyleClass().add("settings-poweroff-button"); powerOffButton.setMaxWidth(Double.MAX_VALUE); updateContent(); } - /** * Returns the button for importing a stock file. * @@ -110,15 +110,6 @@ public Button getOpenFileChooserButton() { return openFileChooserButton; } - /** - * Returns the file chooser for selecting a stock file. - * - * @return the FileChooser - */ - public javafx.stage.FileChooser getFileChooser() { - return fileChooser; - } - /** * Returns whether the user is logged in. * @@ -191,7 +182,11 @@ public Button getPowerOffButton() { return powerOffButton; } - /** Returns the current gradient config as chosen in the UI. */ + /** + * Returns the current gradient configuration as chosen in the appearance section of the UI. + * + * @return the gradient config built from the current color and direction selections + */ public GradientConfig getGradientConfig() { return new GradientConfig( gradientColorA.getValue(), @@ -220,27 +215,26 @@ public void setPath(final Path path) { updateContent(); } - /** * Rebuilds the content of the settings app based on current state. */ public void updateContent() { - VBox layout = new VBox(16); + final VBox layout = new VBox(16); layout.setPadding(new Insets(24, 28, 24, 28)); layout.getStyleClass().add("settings-layout"); layout.getChildren().add(sectionTitle("Audio")); layout.getChildren().add(sliderRow("Master volume", masterVolumeSlider)); - layout.getChildren().add(sliderRow("SFX volume", sfxVolumeSlider)); + layout.getChildren().add(sliderRow("SFX volume", sfxVolumeSlider)); if (!isLoggedIn) { layout.getChildren().add(new Separator()); layout.getChildren().add(sectionTitle("Data")); - String pathText = userSelectedPath != null + final String pathText = userSelectedPath != null ? userSelectedPath.getFileName().toString() : "No file selected"; - Label pathLabel = new Label(pathText); + final Label pathLabel = new Label(pathText); pathLabel.getStyleClass().add("settings-path-label"); layout.getChildren().add(new VBox(8, openFileChooserButton, pathLabel)); @@ -251,15 +245,16 @@ public void updateContent() { layout.getChildren().add(new Separator()); layout.getChildren().add(sectionTitle("Profile")); - HBox nameRow = new HBox(8, playerNameField, applyNameButton); + final HBox nameRow = new HBox(8, playerNameField, applyNameButton); HBox.setHgrow(playerNameField, Priority.ALWAYS); + HBox.setHgrow(nameRow, Priority.ALWAYS); nameRow.setAlignment(Pos.CENTER_LEFT); layout.getChildren().add(labeledRow("Player name", nameRow)); layout.getChildren().add(new Separator()); layout.getChildren().add(sectionTitle("Session")); - HBox sessionRow = new HBox(10, logoutButton, powerOffButton); - HBox.setHgrow(logoutButton, Priority.ALWAYS); + final HBox sessionRow = new HBox(10, logoutButton, powerOffButton); + HBox.setHgrow(logoutButton, Priority.ALWAYS); HBox.setHgrow(powerOffButton, Priority.ALWAYS); layout.getChildren().add(sessionRow); } @@ -268,7 +263,7 @@ public void updateContent() { } private VBox buildGradientSection() { - Region preview = new Region(); + final Region preview = new Region(); preview.setPrefHeight(24); preview.setMaxWidth(Double.MAX_VALUE); preview.getStyleClass().add("settings-gradient-preview"); @@ -277,7 +272,7 @@ private VBox buildGradientSection() { gradientColorB.getValue(), gradientDirection.getValue())); - Runnable refreshPreview = () -> preview.setStyle(buildCssGradient( + final Runnable refreshPreview = () -> preview.setStyle(buildCssGradient( gradientColorA.getValue(), gradientColorB.getValue(), gradientDirection.getValue())); @@ -291,35 +286,40 @@ private VBox buildGradientSection() { HBox.setHgrow(gradientColorA, Priority.ALWAYS); HBox.setHgrow(gradientColorB, Priority.ALWAYS); - Label fromLabel = new Label("From"); - Label toLabel = new Label("To"); + final Label fromLabel = new Label("From"); + final Label toLabel = new Label("To"); fromLabel.getStyleClass().add("settings-row-label"); toLabel.getStyleClass().add("settings-row-label"); - HBox colorRow = new HBox(8, fromLabel, gradientColorA, toLabel, gradientColorB); + final HBox colorRow = new HBox(8, fromLabel, gradientColorA, toLabel, gradientColorB); colorRow.setAlignment(Pos.CENTER_LEFT); gradientDirection.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(gradientDirection, Priority.ALWAYS); + HBox.setHgrow(gradientDirection, Priority.ALWAYS); - HBox controlRow = new HBox(8, gradientDirection, applyGradientButton); + final HBox controlRow = new HBox(8, gradientDirection, applyGradientButton); controlRow.setAlignment(Pos.CENTER_LEFT); - VBox box = new VBox(8, colorRow, controlRow, preview); - return box; + return new VBox(8, colorRow, controlRow, preview); } - /** Converts a direction label to a CSS linear-gradient string. */ + /** + * Converts a direction label to a CSS linear-gradient string. + * + * @param a the start color of the gradient + * @param b the end color of the gradient + * @param direction the human-readable direction label (e.g. "Top to Bottom") + * @return a JavaFX CSS background string for a linear gradient + */ public static String buildCssGradient( final Color a, final Color b, final String direction) { - - String angle = switch (direction) { - case "Bottom → Top" -> "to top"; - case "Left → Right" -> "to right"; - case "Right → Left" -> "to left"; - case "Diagonal ↘" -> "to bottom right"; - case "Diagonal ↙" -> "to bottom left"; - default -> "to bottom"; // "Top → Bottom" + final String angle = switch (direction) { + case "Bottom to Top" -> "to top"; + case "Left to Right" -> "to right"; + case "Right to Left" -> "to left"; + case "Top Left to Bottom Right" -> "to bottom right"; + case "Top Right to Bottom Left" -> "to bottom left"; + default -> "to bottom"; // "Top → Bottom" }; return String.format( "-fx-background-color: linear-gradient(%s, %s, %s);", @@ -328,27 +328,26 @@ public static String buildCssGradient( private static String toHex(final Color c) { return String.format("#%02x%02x%02x", - (int) (c.getRed() * 255), + (int) (c.getRed() * 255), (int) (c.getGreen() * 255), - (int) (c.getBlue() * 255)); + (int) (c.getBlue() * 255)); } - private static Slider createSlider() { - Slider s = new Slider(0, 100, 80); + final Slider s = new Slider(0, 100, 80); s.getStyleClass().add("settings-slider"); HBox.setHgrow(s, Priority.ALWAYS); return s; } private static Label sectionTitle(final String text) { - Label l = new Label(text.toUpperCase()); + final Label l = new Label(text.toUpperCase()); l.getStyleClass().add("settings-section-title"); return l; } private static HBox sliderRow(final String labelText, final Slider slider) { - Label valueLabel = new Label("80%"); + final Label valueLabel = new Label("80%"); valueLabel.getStyleClass().add("settings-value-label"); valueLabel.setMinWidth(36); slider.valueProperty().addListener( @@ -357,11 +356,11 @@ private static HBox sliderRow(final String labelText, final Slider slider) { } private static HBox labeledRow(final String labelText, final Node... nodes) { - Label label = new Label(labelText); + final Label label = new Label(labelText); label.getStyleClass().add("settings-row-label"); label.setMinWidth(120); - HBox row = new HBox(10); + final HBox row = new HBox(10); row.setAlignment(Pos.CENTER_LEFT); row.getChildren().add(label); row.getChildren().addAll(nodes); diff --git a/src/main/resources/css/settings.css b/src/main/resources/css/settings.css index 4958632..5a9dbea 100644 --- a/src/main/resources/css/settings.css +++ b/src/main/resources/css/settings.css @@ -1,113 +1,111 @@ .settings-layout { - -fx-background-color: transparent; + -fx-background-color: transparent; } .settings-section-title { - -fx-font-size: 11px; - -fx-font-weight: bold; - -fx-text-fill: #555f6e; - -fx-padding: 4 0 0 0; + -fx-font-size: 11px; + -fx-font-weight: bold; + -fx-text-fill: #555f6e; + -fx-padding: 4 0 0 0; } .settings-row-label { - -fx-font-size: 13px; - -fx-text-fill: #1a1a1a; + -fx-font-size: 13px; + -fx-text-fill: #1a1a1a; } .settings-value-label { - -fx-font-size: 12px; - -fx-text-fill: #333333; - -fx-min-width: 36px; - -fx-alignment: center-right; + -fx-font-size: 12px; + -fx-text-fill: #333333; + -fx-min-width: 36px; + -fx-alignment: center-right; } .settings-slider .track { - -fx-background-color: #cccccc; - -fx-pref-height: 4px; + -fx-background-color: #cccccc; + -fx-pref-height: 4px; } .settings-slider .thumb { - -fx-background-color: #5b9bd5; - -fx-pref-width: 14px; - -fx-pref-height: 14px; + -fx-background-color: #5b9bd5; + -fx-pref-width: 14px; + -fx-pref-height: 14px; } .settings-text-field { - -fx-background-color: #f5f5f5; - -fx-text-fill: #1a1a1a; - -fx-prompt-text-fill: #999999; - -fx-border-color: #cccccc; - -fx-border-radius: 6; - -fx-background-radius: 6; - -fx-padding: 6 10; + -fx-background-color: #f5f5f5; + -fx-text-fill: #1a1a1a; + -fx-prompt-text-fill: #999999; + -fx-border-color: #cccccc; + -fx-border-radius: 6; + -fx-background-radius: 6; + -fx-padding: 6 10; } .settings-path-label { - -fx-font-size: 11px; - -fx-text-fill: #444444; - -fx-font-style: italic; -} - -.settings-color-picker { - -fx-pref-width: 120px; + -fx-font-size: 11px; + -fx-text-fill: #444444; + -fx-font-style: italic; } .settings-logout-button { - -fx-background-color: #e8f0fe; - -fx-text-fill: #1a1a1a; - -fx-border-color: #b0c4e8; - -fx-border-radius: 6; - -fx-background-radius: 6; - -fx-font-size: 13px; - -fx-padding: 6 16; - -fx-cursor: hand; + -fx-background-color: #e8f0fe; + -fx-text-fill: #1a1a1a; + -fx-border-color: #b0c4e8; + -fx-border-radius: 6; + -fx-background-radius: 6; + -fx-font-size: 13px; + -fx-padding: 6 16; + -fx-cursor: hand; } .settings-logout-button:hover { - -fx-background-color: #d0e0f8; + -fx-background-color: #d0e0f8; } .settings-poweroff-button { - -fx-background-color: #fdecea; - -fx-text-fill: #c0392b; - -fx-border-color: #f0b8b2; - -fx-border-radius: 6; - -fx-background-radius: 6; - -fx-font-size: 13px; - -fx-font-weight: bold; - -fx-padding: 6 16; - -fx-cursor: hand; + -fx-background-color: #fdecea; + -fx-text-fill: #c0392b; + -fx-border-color: #f0b8b2; + -fx-border-radius: 6; + -fx-background-radius: 6; + -fx-font-size: 13px; + -fx-font-weight: bold; + -fx-padding: 6 16; + -fx-cursor: hand; } .settings-poweroff-button:hover { - -fx-background-color: #f8d7d4; + -fx-background-color: #f8d7d4; } .settings-layout .color-picker { - -fx-pref-width: 110px; - -fx-max-width: 110px; + -fx-pref-width: 0; + -fx-max-width: infinity; } .settings-layout .color-picker .color-picker-label { - -fx-text-fill: #1a1a1a; + -fx-text-fill: #1a1a1a; } .settings-combo { - -fx-pref-width: 0; - -fx-background-color: #f5f5f5; - -fx-border-color: #cccccc; - -fx-border-radius: 6; - -fx-background-radius: 6; - -fx-text-fill: #1a1a1a; + -fx-pref-width: 0; + -fx-max-width: infinity; + -fx-background-color: #f5f5f5; + -fx-border-color: #cccccc; + -fx-border-radius: 6; + -fx-background-radius: 6; + -fx-text-fill: #1a1a1a; } .settings-combo .list-cell { - -fx-text-fill: #1a1a1a; + -fx-text-fill: #1a1a1a; } .settings-gradient-preview { - -fx-background-radius: 6; - -fx-border-radius: 6; - -fx-border-color: #cccccc; - -fx-border-width: 1; + -fx-background-radius: 6; + -fx-border-radius: 6; + -fx-border-color: #cccccc; + -fx-border-width: 1; + -fx-min-height: 24px; } \ No newline at end of file