Skip to content

Commit

Permalink
Merge pull request #108 from einaskoi/per/popups
Browse files Browse the repository at this point in the history
refactor the popups and adds reloading of the styling when updated
  • Loading branch information
einaskoi authored May 14, 2026
2 parents 03c6273 + 0abb1df commit 6064d35
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import edu.ntnu.idi.idatt2003.gruppe42.View.Popup;
import java.util.List;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;

/**
* Manages popup visibility, dragging, and bounds-clamping.
Expand Down Expand Up @@ -56,30 +56,27 @@ public List<Popup> getPopups() {
return popups;
}

public void bindOpenButton(Button button, App type){
public void bindOpenButton(Button button, App type) {
button.setOnAction(event -> show(type));
}

private void makeDraggable(Popup popup) {
BorderPane root = popup.getRoot();
StackPane root = popup.getRoot();
double[] offset = new double[2];
double[] parentBounds = new double[2]; // cache here
double[] maxXY = new double[2];

popup.getHeader().setOnMousePressed(event -> {
offset[0] = event.getSceneX() - root.getLayoutX();
offset[1] = event.getSceneY() - root.getLayoutY();
// snapshot bounds once on press, not on every drag tick
if (root.getParent() instanceof Pane parent) {
parentBounds[0] = parent.getWidth() - popup.getWidth();
parentBounds[1] = parent.getHeight() - popup.getHeight();
maxXY[0] = parent.getWidth() - root.getWidth();
maxXY[1] = parent.getHeight() - root.getHeight();
}
});

popup.getHeader().setOnMouseDragged(event -> {
root.setLayoutX(Math.max(0, Math.min(
event.getSceneX() - offset[0], parentBounds[0])));
root.setLayoutY(Math.max(0, Math.min(
event.getSceneY() - offset[1], parentBounds[1])));
root.setLayoutX(Math.max(0, Math.min(event.getSceneX() - offset[0], maxXY[0])));
root.setLayoutY(Math.max(0, Math.min(event.getSceneY() - offset[1], maxXY[1])));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ public class StartViewController {
"Hustler G",
"Chin Checker",
"Sparkling Water Enthusiast",
"The Talisman"
"The Talisman",
"Epstein follower",
"Looks Maxer",
"Mentioned in Epstein files",
"Baby Oil Hoarder",
"Roofie Supplier",
"Hentai virus"
);

public StartViewController(final Millions application, final StartView startView) {
Expand Down
141 changes: 101 additions & 40 deletions src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Popup.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;

Expand All @@ -21,10 +24,14 @@
*/
public abstract class Popup {

private static final boolean DEV_CSS_RELOAD = true;
private static final double ARC = 16;

private final int width;
private final int height;
private final App type;

private final StackPane wrapper;
private final BorderPane root;
private final HBox header;
private final Button closeButton;
Expand All @@ -47,33 +54,95 @@ protected Popup(int width, int height, int x, int y, App type) {
scrollPane.setFitToHeight(true);

closeButton = buildCloseButton();
header = buildHeader(closeButton, titleString());
header = buildHeader(closeButton, type.getDisplayName());

root = new BorderPane();
root.getStyleClass().add("popup-root");
root.getStylesheets().addAll(
resource("/css/global.css"),
resource("/css/popup.css"),
resource("/css/apps.css")
);
root.setTop(header);
root.setCenter(scrollPane);
root.setLayoutX(x);
root.setLayoutY(y);
root.setManaged(false);
root.setVisible(false);
root.autosize();
root.setClip(roundedClip());
root.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> root.toFront());
root.setClip(buildClip(root));

Rectangle borderOverlay = buildBorderOverlay();

wrapper = new StackPane(root, borderOverlay);
wrapper.setLayoutX(x);
wrapper.setLayoutY(y);
wrapper.setManaged(false);
wrapper.setVisible(false);
wrapper.setEffect(new DropShadow(14, 0, 4, Color.rgb(0, 0, 0, 0.25)));
wrapper.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> wrapper.toFront());

loadStylesheets();

if (DEV_CSS_RELOAD) {
wrapper.setFocusTraversable(true);
wrapper.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.R) {
loadStylesheets();
System.out.println("[CSS] Popup stylesheets reloaded");
}
});
}
}

private Rectangle buildClip(BorderPane target) {
Rectangle clip = new Rectangle();
clip.setArcWidth(ARC);
clip.setArcHeight(ARC);
clip.widthProperty().bind(target.widthProperty());
clip.heightProperty().bind(target.heightProperty());
return clip;
}

private Rectangle buildBorderOverlay() {
Rectangle border = new Rectangle();
border.getStyleClass().add("popup-border-overlay");
border.setArcWidth(ARC);
border.setArcHeight(ARC);
border.widthProperty().bind(root.widthProperty());
border.heightProperty().bind(root.heightProperty());
border.setFill(Color.TRANSPARENT);
border.setMouseTransparent(true);
return border;
}

private void loadStylesheets() {
root.getStylesheets().clear();
try {
if (DEV_CSS_RELOAD) {
String projectDir = System.getProperty("user.dir");
root.getStylesheets().addAll(
devCopyToTemp(projectDir + "/src/main/resources/css/global.css"),
devCopyToTemp(projectDir + "/src/main/resources/css/popup.css"),
devCopyToTemp(projectDir + "/src/main/resources/css/apps.css")
);
} else {
root.getStylesheets().addAll(
resource("/css/global.css"),
resource("/css/popup.css"),
resource("/css/apps.css")
);
}
} catch (Exception e) {
System.err.println("[CSS] Failed to load popup stylesheets: " + e.getMessage());
}
}

private String devCopyToTemp(final String sourcePath) throws Exception {
java.nio.file.Path src = java.nio.file.Paths.get(sourcePath);
java.nio.file.Path tmp = java.nio.file.Files.createTempFile("css_", ".css");
java.nio.file.Files.copy(src, tmp, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
tmp.toFile().deleteOnExit();
return tmp.toUri().toString();
}

private Button buildCloseButton() {
Button btn = new Button();
btn.setShape(new Circle(6));
btn.setMinSize(12, 12);
btn.setMaxSize(12, 12);
btn.getStyleClass().add("popup-close-button");
return btn;
Button button = new Button();
button.setShape(new Circle(6));
button.setMinSize(12, 12);
button.setMaxSize(12, 12);
button.getStyleClass().add("popup-close-button");
return button;
}

private HBox buildHeader(Button closeButton, String title) {
Expand All @@ -91,41 +160,33 @@ private HBox buildHeader(Button closeButton, String title) {
return hbox;
}

private String titleString() {
return type.getDisplayName();
}

private Rectangle roundedClip() {
Rectangle clip = new Rectangle();
clip.setArcWidth(20);
clip.setArcHeight(20);
clip.widthProperty().bind(root.widthProperty());
clip.heightProperty().bind(root.heightProperty());
return clip;
}

private String resource(String path) {
private String resource(final String path) {
return getClass().getResource(path).toExternalForm();
}

/**
* Binds this popup's layoutX/Y so it is always centered inside its parent.
* Safe to call before or after the wrapper is added to the scene graph.
*/
public void centerInParent() {
root.parentProperty().addListener((obs, oldParent, parent) -> {
if (parent instanceof Pane pane) {
root.layoutXProperty().bind(pane.widthProperty().subtract(width).divide(2));
root.layoutYProperty().bind(pane.heightProperty().subtract(height).divide(2));
Runnable bind = () -> {
if (wrapper.getParent() instanceof Pane pane) {
wrapper.layoutXProperty().bind(
pane.widthProperty().subtract(wrapper.widthProperty()).divide(2));
wrapper.layoutYProperty().bind(
pane.heightProperty().subtract(wrapper.heightProperty()).divide(2));
}
});
};
bind.run();
wrapper.parentProperty().addListener((obs, oldParent, newParent) -> bind.run());
}

public App getType() {
return type;
}

public BorderPane getRoot() {
return root;
public StackPane getRoot() {
return wrapper;
}

public HBox getHeader() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
*/
public final class DesktopView {

// ── DEV FLAG ─────────────────────────────────────────────────────────────
// Set to false (or delete this + all DEV_CSS_RELOAD blocks) before release.
private static final boolean DEV_CSS_RELOAD = true;
// ─────────────────────────────────────────────────────────────────────────

/**
* Z-stack of modal levels, ordered from lowest to highest. Each value
* is its own named token; no numeric z-index lives anywhere else.
Expand Down Expand Up @@ -73,16 +78,59 @@ public BorderPane getRoot() {
bottomBar = createBottomBar();
root.setCenter(gridArea);
root.setBottom(bottomBar);
root.getStylesheets().addAll(
resource("/css/global.css"),
resource("/css/desktop.css"),
resource("/css/apps.css")
);
root.getStyleClass().add("desktop-root");

loadStylesheets();

// DEV_CSS_RELOAD: press R to hot-reload CSS from source without restarting
if (DEV_CSS_RELOAD) {
root.setFocusTraversable(true);
root.sceneProperty().addListener((obs, oldScene, newScene) -> {
if (newScene != null) root.requestFocus();
});
root.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.R) {
loadStylesheets();
System.out.println("[CSS] Stylesheets reloaded");
}
});
}
}
return root;
}

private void loadStylesheets() {
root.getStylesheets().clear();
try {
if (DEV_CSS_RELOAD) {
// DEV_CSS_RELOAD: reads directly from source, copies to temp to bust JavaFX cache
String projectDir = System.getProperty("user.dir");
root.getStylesheets().addAll(
devCopyToTemp(projectDir + "/src/main/resources/css/global.css"),
devCopyToTemp(projectDir + "/src/main/resources/css/desktop.css"),
devCopyToTemp(projectDir + "/src/main/resources/css/apps.css")
);
} else {
root.getStylesheets().addAll(
resource("/css/global.css"),
resource("/css/desktop.css"),
resource("/css/apps.css")
);
}
} catch (Exception e) {
System.err.println("[CSS] Failed to load stylesheets: " + e.getMessage());
}
}

// DEV_CSS_RELOAD: copies a CSS file to a unique temp path so JavaFX can't cache it
private String devCopyToTemp(final String sourcePath) throws Exception {
java.nio.file.Path src = java.nio.file.Paths.get(sourcePath);
java.nio.file.Path tmp = java.nio.file.Files.createTempFile("css_", ".css");
java.nio.file.Files.copy(src, tmp, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
tmp.toFile().deleteOnExit();
return tmp.toUri().toString();
}

// Layer registration: the controller adds nodes to the scene graph in
// the exact z-order required for the layering rules to work.

Expand Down Expand Up @@ -290,8 +338,7 @@ private HBox buildHeartsBox(final Player player) {
hearts[i].getStyleClass().add(active ? "heart-active" : "heart-empty");
}
};
player.getLivesProperty().addListener((
obs, ov, nv) -> Platform.runLater(refresh));
player.getLivesProperty().addListener((obs, ov, nv) -> Platform.runLater(refresh));
refresh.run();

return box;
Expand All @@ -303,10 +350,10 @@ private HBox createStatusBox() {
TimeAndWeatherController twc = controller.getTimeAndWeatherController();

Label weather = styledLabel("desktop-label");
Label temp = styledLabel("desktop-label");
Label day = styledLabel("desktop-label-bold");
Label week = styledLabel("desktop-label-bold");
Label clock = styledLabel("desktop-label-bold");
Label temp = styledLabel("desktop-label");
Label day = styledLabel("desktop-label-bold");
Label week = styledLabel("desktop-label-bold");
Label clock = styledLabel("desktop-label-bold");

weather.setText(twc.weatherProperty().get());
temp.setText(twc.temperatureProperty().get() + "°C");
Expand Down Expand Up @@ -346,4 +393,4 @@ private String resource(final String path) {
public Button getNextWeekButton() {
return nextWeekButton;
}
}
}
Loading

0 comments on commit 6064d35

Please sign in to comment.