diff --git a/src/main/java/dk/cs/aau/huppaal/controllers/ComponentController.java b/src/main/java/dk/cs/aau/huppaal/controllers/ComponentController.java index 8bb4bc9d..4c180cf3 100644 --- a/src/main/java/dk/cs/aau/huppaal/controllers/ComponentController.java +++ b/src/main/java/dk/cs/aau/huppaal/controllers/ComponentController.java @@ -392,7 +392,7 @@ private void initializeComponentContextMenu() { return; } - contextMenu = new DropDownMenu(root, dropDownMenuHelperCircle, 230, true); + contextMenu = new DropDownMenu(dropDownMenuHelperCircle, 230, true); contextMenu.addClickableListElement("Add Location", event -> { contextMenu.close(); @@ -464,7 +464,7 @@ private void initializeComponentContextMenu() { //If-statement added to avoid empty submenu being added and appearing as a white box if(HUPPAAL.getProject().getComponents().size() > 1){ - subMenu = new DropDownMenu(root, dropDownMenuHelperCircle, 150, false); + subMenu = new DropDownMenu(dropDownMenuHelperCircle, 150, false); HUPPAAL.getProject().getComponents().forEach(c -> { if (!c.equals(component)) { subMenu.addClickableListElement(c.getName(), event -> { @@ -491,7 +491,7 @@ private void initializeComponentContextMenu() { }); } else { - subMenu = new DropDownMenu(root, dropDownMenuHelperCircle, 150, false); + subMenu = new DropDownMenu(dropDownMenuHelperCircle, 150, false); subMenu.addClickableAndDisableableListElement("No Subcomponents", new SimpleBooleanProperty(true), event -> {}); } @@ -557,7 +557,7 @@ private void initializeFinishEdgeContextMenu(final Edge unfinishedEdge) { locationAware.yProperty().set(y); }; - finishEdgeContextMenu = new DropDownMenu(root, dropDownMenuHelperCircle, 230, true); + finishEdgeContextMenu = new DropDownMenu(dropDownMenuHelperCircle, 230, true); finishEdgeContextMenu.addListElement("Finish edge in a:"); @@ -621,7 +621,7 @@ private void initializeFinishEdgeContextMenu(final Edge unfinishedEdge) { }, "Finished edge '" + unfinishedEdge + "' by adding '" + jork + "' to component '" + component.getName() + "'", "add-circle"); }); - final DropDownMenu subMenu = new DropDownMenu(root, dropDownMenuHelperCircle, 150, false); + final DropDownMenu subMenu = new DropDownMenu(dropDownMenuHelperCircle, 150, false); HUPPAAL.getProject().getComponents().forEach(c -> { if (!c.equals(component)) { subMenu.addClickableListElement(c.getName(), event -> { diff --git a/src/main/java/dk/cs/aau/huppaal/controllers/EdgeController.java b/src/main/java/dk/cs/aau/huppaal/controllers/EdgeController.java index a2f68bc1..04193877 100644 --- a/src/main/java/dk/cs/aau/huppaal/controllers/EdgeController.java +++ b/src/main/java/dk/cs/aau/huppaal/controllers/EdgeController.java @@ -1,5 +1,6 @@ package dk.cs.aau.huppaal.controllers; +import dk.cs.aau.huppaal.HUPPAAL; import dk.cs.aau.huppaal.abstractions.*; import dk.cs.aau.huppaal.code_analysis.CodeAnalysis; import dk.cs.aau.huppaal.code_analysis.Nearable; @@ -31,7 +32,6 @@ import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Pane; import javafx.scene.shape.Circle; import javafx.util.Duration; @@ -513,9 +513,7 @@ public void onChanged(final Change c) { links.forEach((link) -> link.setOnMousePressed(event -> { if (event.isSecondaryButtonDown() && getComponent().getUnfinishedEdge() == null) { - event.consume(); - - final DropDownMenu dropDownMenu = new DropDownMenu(((Pane) edgeRoot.getParent().getParent().getParent().getParent()), dropDownMenuHelperCircle, 230, true); + final DropDownMenu dropDownMenu = new DropDownMenu(edgeRoot, 230, true); addEdgePropertyRow(dropDownMenu, "Add Select", Edge.PropertyType.SELECTION, link); addEdgePropertyRow(dropDownMenu, "Add Guard", Edge.PropertyType.GUARD, link); diff --git a/src/main/java/dk/cs/aau/huppaal/controllers/HUPPAALController.java b/src/main/java/dk/cs/aau/huppaal/controllers/HUPPAALController.java index c520df1d..01a03f17 100644 --- a/src/main/java/dk/cs/aau/huppaal/controllers/HUPPAALController.java +++ b/src/main/java/dk/cs/aau/huppaal/controllers/HUPPAALController.java @@ -36,11 +36,13 @@ import javafx.scene.shape.Rectangle; import javafx.scene.text.Text; import javafx.stage.DirectoryChooser; +import javafx.stage.WindowEvent; import javafx.stage.FileChooser; import javafx.util.Duration; import javafx.util.Pair; import org.kordamp.ikonli.javafx.FontIcon; +import java.awt.event.WindowListener; import java.io.File; import java.io.IOException; import java.net.URL; diff --git a/src/main/java/dk/cs/aau/huppaal/controllers/JorkController.java b/src/main/java/dk/cs/aau/huppaal/controllers/JorkController.java index 8164af7f..e2fa01c8 100644 --- a/src/main/java/dk/cs/aau/huppaal/controllers/JorkController.java +++ b/src/main/java/dk/cs/aau/huppaal/controllers/JorkController.java @@ -27,7 +27,6 @@ import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Pane; import javafx.scene.shape.Path; import java.net.URL; @@ -121,10 +120,10 @@ private void initializeMouseControls() { private void showContextMenu() { - final DropDownMenu contextMenu = new DropDownMenu(((Pane) root.getParent().getParent().getParent().getParent()), root, 230, true); + final DropDownMenu contextMenu = new DropDownMenu(root, 230, true); contextMenu.addClickableListElement("Draw edge", - (event) -> { + (mouseEvent) -> { final Edge newEdge = new Edge(getJork()); KeyboardTracker.registerKeybind(KeyboardTracker.ABANDON_EDGE, new Keybind(new KeyCodeCombination(KeyCode.ESCAPE), () -> { @@ -144,7 +143,7 @@ private void showContextMenu() { contextMenu.addSpacerElement(); - contextMenu.addClickableListElement("Delete", (mouseEvent -> { + contextMenu.addClickableListElement("Delete", (event -> { final Component component = CanvasController.getActiveComponent(); final Jork jork = getJork(); diff --git a/src/main/java/dk/cs/aau/huppaal/controllers/LocationController.java b/src/main/java/dk/cs/aau/huppaal/controllers/LocationController.java index 30a9f9bd..572fb271 100644 --- a/src/main/java/dk/cs/aau/huppaal/controllers/LocationController.java +++ b/src/main/java/dk/cs/aau/huppaal/controllers/LocationController.java @@ -31,7 +31,6 @@ import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; @@ -115,7 +114,7 @@ public void initializeDropDownMenu() { if (dropDownMenuInitialized) return; dropDownMenuInitialized = true; - dropDownMenu = new DropDownMenu(((Pane) root.getParent().getParent().getParent()), root, 230, true); + dropDownMenu = new DropDownMenu(root, 230, true); dropDownMenu.addClickableListElement("Draw edge", (event) -> { diff --git a/src/main/java/dk/cs/aau/huppaal/controllers/NailController.java b/src/main/java/dk/cs/aau/huppaal/controllers/NailController.java index 248d9b91..c8db7150 100644 --- a/src/main/java/dk/cs/aau/huppaal/controllers/NailController.java +++ b/src/main/java/dk/cs/aau/huppaal/controllers/NailController.java @@ -24,7 +24,6 @@ import javafx.scene.Group; import javafx.scene.control.Label; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Pane; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; @@ -73,7 +72,7 @@ public void initialize(final URL location, final ResourceBundle resources) { private void showContextMenu() { - final DropDownMenu contextMenu = new DropDownMenu(((Pane) root.getParent().getParent().getParent().getParent()), root, 230, true); + final DropDownMenu contextMenu = new DropDownMenu(root, 230, true); contextMenu.addClickableListElement("Delete", (mouseEvent -> { final Nail nail = getNail(); diff --git a/src/main/java/dk/cs/aau/huppaal/controllers/ProjectPaneController.java b/src/main/java/dk/cs/aau/huppaal/controllers/ProjectPaneController.java index a7df3b68..ca753e0b 100644 --- a/src/main/java/dk/cs/aau/huppaal/controllers/ProjectPaneController.java +++ b/src/main/java/dk/cs/aau/huppaal/controllers/ProjectPaneController.java @@ -74,7 +74,7 @@ private void sortPresentations() { private void initializeColorSelector(final FilePresentation filePresentation) { final JFXRippler moreInformation = (JFXRippler) filePresentation.lookup("#moreInformation"); final int listWidth = 230; - final DropDownMenu moreInformationDropDown = new DropDownMenu(root, moreInformation, listWidth, true); + final DropDownMenu moreInformationDropDown = new DropDownMenu(moreInformation, listWidth, true); final Component component = filePresentation.getComponent(); moreInformationDropDown.addListElement("Configuration"); diff --git a/src/main/java/dk/cs/aau/huppaal/controllers/SubComponentController.java b/src/main/java/dk/cs/aau/huppaal/controllers/SubComponentController.java index 4a122ddf..452951f8 100644 --- a/src/main/java/dk/cs/aau/huppaal/controllers/SubComponentController.java +++ b/src/main/java/dk/cs/aau/huppaal/controllers/SubComponentController.java @@ -78,7 +78,7 @@ private void initializeDropDownMenu() { if (dropDownMenuInitialized) return; dropDownMenuInitialized = true; - dropDownMenu = new DropDownMenu(((Pane) root.getParent().getParent().getParent()), root, 230, true); + dropDownMenu = new DropDownMenu(root, 230, true); dropDownMenu.addClickableListElement("Open in canvas", event -> { CanvasController.setActiveComponent(getSubComponent().getComponent()); @@ -106,7 +106,7 @@ private void initializeDropDownMenu() { dropDownMenu.addSpacerElement(); - final DropDownMenu subMenu = new DropDownMenu(((Pane) root.getParent().getParent().getParent()), root, 150, false); + final DropDownMenu subMenu = new DropDownMenu(root, 150, false); HUPPAAL.getProject().getComponents().forEach(c -> { if (!c.equals(getParentComponent())) { subMenu.addClickableListElement(c.getName(), event -> { diff --git a/src/main/java/dk/cs/aau/huppaal/presentations/DropDownMenu.java b/src/main/java/dk/cs/aau/huppaal/presentations/DropDownMenu.java index 51971e4c..f94856e7 100644 --- a/src/main/java/dk/cs/aau/huppaal/presentations/DropDownMenu.java +++ b/src/main/java/dk/cs/aau/huppaal/presentations/DropDownMenu.java @@ -1,5 +1,6 @@ package dk.cs.aau.huppaal.presentations; +import dk.cs.aau.huppaal.HUPPAAL; import dk.cs.aau.huppaal.utility.colors.Color; import dk.cs.aau.huppaal.utility.colors.EnabledColor; import com.jfoenix.controls.JFXPopup; @@ -13,10 +14,12 @@ import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; import javafx.scene.input.MouseEvent; import javafx.scene.layout.*; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; +import javafx.stage.Screen; import javafx.util.Duration; import org.kordamp.ikonli.javafx.FontIcon; @@ -40,10 +43,10 @@ public class DropDownMenu { private final SimpleBooleanProperty isHoveringMenu = new SimpleBooleanProperty(false); private final SimpleBooleanProperty showSubMenu = new SimpleBooleanProperty(false); private final SimpleBooleanProperty canIShowSubMenu = new SimpleBooleanProperty(false); - private StackPane subMenuContent; + private ScrollPane subMenuContent; private final Node source; - public DropDownMenu(final Pane container, final Node source, final int width, final boolean closeOnMouseExit) { + public DropDownMenu(final Node source, final int width, final boolean closeOnMouseExit) { this.width = width; this.source = source; @@ -88,7 +91,34 @@ public void close() { } public void show(final JFXPopup.PopupVPosition vAlign, final JFXPopup.PopupHPosition hAlign, final double initOffsetX, final double initOffsetY) { - popup.show(this.source, vAlign, hAlign, initOffsetX, initOffsetY); + //Needed to update the location of the popup before it is displayed + this.flashDropdown(); + + //Check if the dropdown will appear outside the screen and change the offset accordingly + double offsetX = initOffsetX; + double offsetY = initOffsetY; + double distEdgeX = Screen.getPrimary().getBounds().getWidth() - (popup.getAnchorX() + offsetX); + double distEdgeY = Screen.getPrimary().getBounds().getHeight() - (popup.getAnchorY() + offsetY); + + //The additional 20 is added for margin + if(distEdgeX < width + 20){ + offsetX -= (width + 20) - distEdgeX; + } + + if(distEdgeY < list.getHeight() + 20){ + offsetY -= (list.getHeight() + 20) - distEdgeY; + } + + //Set the x-coordinate of the potential submenu to avoid screen overflow + if(subMenuContent != null){ + if(Screen.getPrimary().getBounds().getWidth() - (popup.getAnchorX() + width) < width){ + subMenuContent.setTranslateX(- width); + } else{ + subMenuContent.setTranslateX(width); + } + } + + popup.show(this.source, vAlign, hAlign, offsetX, offsetY); } public void addListElement(final String s) { @@ -146,18 +176,22 @@ public void addSubMenu(final String s, final DropDownMenu subMenu, final int off label.getStyleClass().add("body2"); label.setMinWidth(width); - subMenuContent = subMenu.content; + subMenuContent = new ScrollPane(subMenu.content.getChildren().get(0)); + subMenuContent.setMinHeight(340 - offset); + subMenuContent.setMinWidth(width); + subMenuContent.setFitToWidth(true); + subMenuContent.setFitToHeight(true); + subMenuContent.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + subMenuContent.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED); + subMenuContent.setTranslateY(offset + subMenuContent.getMinHeight()/2); if (!this.content.getChildren().contains(subMenuContent)) { - subMenuContent.setStyle("-fx-padding: 0 0 0 5;"); subMenuContent.setMinWidth(subMenuContent.getMinWidth() + 1); subMenuContent.setMaxWidth(subMenuContent.getMinWidth() + 1); - subMenuContent.setTranslateX(width - 40); this.content.getChildren().add(subMenuContent); } - subMenuContent.setTranslateY(offset); - subMenuContent.setOpacity(0); + final Runnable showHideSubMenu = () -> { if (showSubMenu.get() || isHoveringSubMenu.get()) { subMenuContent.setOpacity(1); @@ -166,14 +200,15 @@ public void addSubMenu(final String s, final DropDownMenu subMenu, final int off } }; - showSubMenu.addListener((obs, oldShow, newShow) -> showHideSubMenu.run()); - isHoveringSubMenu.addListener((obs, oldHovering, newHovering) -> showHideSubMenu.run()); + showSubMenu.addListener((obs) -> showHideSubMenu.run()); + isHoveringSubMenu.addListener((obs) -> showHideSubMenu.run()); subMenuContent.setOnMouseEntered(event -> { if (canIShowSubMenu.get()) { isHoveringSubMenu.set(true); } }); + subMenuContent.setOnMouseExited(event -> { isHoveringSubMenu.set(false); }); @@ -415,4 +450,9 @@ public interface HasColor { ObjectProperty colorIntensityProperty(); } + + private void flashDropdown() { + popup.show(this.source, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, 0, 0); + this.close(); + } } diff --git a/src/main/java/dk/cs/aau/huppaal/presentations/QueryPresentation.java b/src/main/java/dk/cs/aau/huppaal/presentations/QueryPresentation.java index eb1ab182..058d78aa 100644 --- a/src/main/java/dk/cs/aau/huppaal/presentations/QueryPresentation.java +++ b/src/main/java/dk/cs/aau/huppaal/presentations/QueryPresentation.java @@ -82,7 +82,7 @@ private void initializeDetailsButton() { detailsButton.setRipplerFill(Color.GREY.getColor(Color.Intensity.I500)); detailsButton.setMaskType(JFXRippler.RipplerMask.CIRCLE); - final DropDownMenu dropDownMenu = new DropDownMenu((Pane) getParent(), detailsButton, 230, true); + final DropDownMenu dropDownMenu = new DropDownMenu(detailsButton, 230, true); dropDownMenu.addTogglableListElement("Run periodically", query.isPeriodicProperty(), event -> { // Toggle the property