package nl.utwente.viskell.ui; import javafx.geometry.Bounds; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.geometry.Side; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.ContextMenu; import javafx.scene.input.TouchEvent; import javafx.scene.layout.*; import java.util.Map; import java.util.TreeMap; /** * MainOverlay creates a stack of panes, the ToplevelPane with contents, menu button, zoom buttons, and touch overlay */ public class MainOverlay extends StackPane { public static final String MENU_LABEL = "\u2630"; private final Pane touchOverlay; private final Map<Integer, TouchDisplay> circleByTouchId; private final ToplevelPane toplevelPane; public MainOverlay(ToplevelPane toplevelPane) { super(); this.toplevelPane = toplevelPane; touchOverlay = makeTouchOverlay(); circleByTouchId = new TreeMap<>(); addEventFilter(TouchEvent.TOUCH_PRESSED, event -> { int touchId = event.getTouchPoint().getId(); Node target = (Node) event.getTarget(); Bounds bounds = target.localToScene(target.getBoundsInLocal()); double x = event.getTouchPoint().getSceneX(); double y = event.getTouchPoint().getSceneY(); TouchDisplay circle = new TouchDisplay(x, y, bounds, touchId); circleByTouchId.put(touchId, circle); touchOverlay.getChildren().add(circle); circle.relocate(x, y); }); addEventFilter(TouchEvent.TOUCH_MOVED, event -> { int touchId = event.getTouchPoint().getId(); double x = event.getTouchPoint().getX(); double y = event.getTouchPoint().getY(); TouchDisplay circle = circleByTouchId.get(touchId); circle.moveTouchPoint(x, y); }); addEventFilter(TouchEvent.TOUCH_RELEASED, event -> { int touchId = event.getTouchPoint().getId(); TouchDisplay circle = circleByTouchId.get(touchId); touchOverlay.getChildren().remove(circle); }); MenuActions menuActions = new MenuActions(this, toplevelPane); Pane zoomBar = makeZoomPane(menuActions); StackPane.setAlignment(zoomBar, Pos.BOTTOM_CENTER); getChildren().setAll(this.toplevelPane, zoomBar, getMenu(menuActions), touchOverlay); } /** * Create the menu to be shown. If we are running on an OS that can display a top menu, then create that * otherwise create an on-screen context menu * * @return menu to show */ private Region getMenu(MenuActions menuActions) { Region menu; final String os = System.getProperty("os.name"); if ((os != null) && os.startsWith("Mac")) { menu = new MacTopMenu(menuActions); } else { menu = makeMenuPane(menuActions); StackPane.setAlignment(menu, Pos.TOP_LEFT); } return menu; } /** * Get the ToplevelPane where all content is displayed * * @return ToplevelPane */ public ToplevelPane getToplevelPane() { return this.toplevelPane; } /** * Create an invisible Pane that can be used to display touch events for debugging * * @return Touch Pane */ private Pane makeTouchOverlay() { Pane touch = new Pane(); // touch overlay shouldn't receive events touch.setDisable(true); // touch overlay is invisible by default touch.setVisible(false); return touch; } /** * Make a Pane that contains a Context menu that can be displayed on-screen * * @param menuActions that can be performed * * @return Menu Pane */ private Pane makeMenuPane(MenuActions menuActions) { ContextMenu burgerMenu = new GlobalContextMenu(menuActions); Button menu = new Button(MENU_LABEL); menu.setFocusTraversable(false); menu.setContextMenu(burgerMenu); menu.setOnAction(e -> menu.getContextMenu().show(menu, Side.BOTTOM, 0, 10)); FlowPane toolBar = new FlowPane(10, 0, menu); toolBar.setMaxHeight(40); toolBar.setMaxWidth(40); // workaround to make it not confiscate the whole top of the screen toolBar.setPadding(new Insets(10)); toolBar.getStyleClass().add("overlayButtons"); return toolBar; } /** * Make a Pane that contains buttons for zoom in and zoom out * * @param menuActions that can be performed, including Zoom In and Zoom Out actions * * @return Zoom Pane */ private Pane makeZoomPane(MenuActions menuActions) { Button zoomIn = new Button("+"); zoomIn.setOnAction(menuActions::zoomIn); Button zoomOut = new Button("–"); zoomOut.setOnAction(menuActions::zoomOut); FlowPane zoomBar = new FlowPane(10, 0, zoomIn, zoomOut); zoomBar.setPrefSize(100, 20); zoomBar.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); zoomBar.setPadding(new Insets(10)); zoomBar.getStyleClass().add("overlayButtons"); return zoomBar; } /** * Set the touch overlay plane to be visible (so that touch events can be displayed) or invisible * * @param visible - new visibility state of the touch overlay plance */ public void setTouchOverlayVisible(boolean visible) { this.touchOverlay.setVisible(visible); } }