/******************************************************************************* * Copyright (c) 2014, 2016 itemis AG and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Matthias Wienand (itemis AG) - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.fx.examples; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import org.eclipse.gef.fx.anchors.AnchorKey; import org.eclipse.gef.fx.anchors.DynamicAnchor; import org.eclipse.gef.fx.anchors.IAnchor; import org.eclipse.gef.fx.nodes.Connection; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableSet; import javafx.event.Event; import javafx.event.EventHandler; import javafx.event.EventType; import javafx.geometry.Point2D; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.ScrollPane; import javafx.scene.effect.Bloom; import javafx.scene.effect.BoxBlur; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; public class MouseDragSnippet extends AbstractFxExample { public static void main(String[] args) { launch(); } private ChangeListener<? super Number> sceneSizeChanged = new ChangeListener<Number>() { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { onSceneSizeChange(scene.getWidth(), scene.getHeight()); } }; private Pane contentLayer; private Pane handleLayer; private Scene scene; private EventHandler<? super MouseEvent> pressedHandler = new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { onMousePress(event); } }; private Node pressed; private EventHandler<? super MouseEvent> mouseFilter = new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { onMouseEvent(event); } }; private Point2D startLayoutPosition; private Point2D startMousePosition; private ObservableSet<Node> nodesUnderMouse = FXCollections .observableSet(new HashSet<Node>()); private Map<Node, IAnchor> anchors = new HashMap<>(); private Pane feedbackLayer; private StackPane stackPane; public MouseDragSnippet() { super("FX MouseDrag Snippet"); } @Override public Scene createScene() { // layers contentLayer = new Pane(); contentLayer.setPickOnBounds(true); handleLayer = new Pane(); handleLayer.setPickOnBounds(false); feedbackLayer = new Pane(); feedbackLayer.setPickOnBounds(false); feedbackLayer.setMouseTransparent(true); stackPane = new StackPane(); stackPane.getChildren().addAll(contentLayer, handleLayer, feedbackLayer); // scrolling ScrollPane scrollPane = new ScrollPane(); Group scrollPaneContent = new Group(stackPane); scrollPaneContent.setAutoSizeChildren(false); scrollPane.setContent(scrollPaneContent); // scene scene = new Scene(scrollPane, 800, 600); scene.widthProperty().addListener(sceneSizeChanged); scene.heightProperty().addListener(sceneSizeChanged); scene.addEventFilter(MouseEvent.ANY, mouseFilter); return scene; } private Node draggable(Node node) { node.addEventHandler(MouseEvent.MOUSE_PRESSED, pressedHandler); return node; } private Node generate(double w, double h) { double rx = Math.random() * (w - 100); double ry = Math.random() * (h - 100); double rw = Math.random() * 90 + 10; double rh = Math.random() * 90 + 10; Rectangle rectangle = new Rectangle(0, 0, rw, rh); rectangle.setLayoutX(rx); rectangle.setLayoutY(ry); rectangle.setFill( new Color(Math.random(), Math.random(), Math.random(), 0.5)); rectangle.setStroke(Color.TRANSPARENT); return rectangle; } protected void onMouseEvent(MouseEvent event) { if (pressed == null) { // no processing if no node is pressed return; } // node is pressed, process all mouse events EventType<? extends Event> type = event.getEventType(); if (type.equals(MouseEvent.MOUSE_RELEASED)) { System.out.println("release " + pressed); pressed.setEffect(null); IAnchor ifxAnchor = anchors.get(pressed); if (ifxAnchor != null) { Set<AnchorKey> keys = ifxAnchor.positionsUnmodifiableProperty().keySet(); for (AnchorKey key : keys) { key.getAnchored().setEffect(new BoxBlur()); } } pressed = null; nodesUnderMouse.clear(); } else if (type.equals(MouseEvent.MOUSE_DRAGGED)) { double dx = event.getSceneX() - startMousePosition.getX(); double dy = event.getSceneY() - startMousePosition.getY(); pressed.setLayoutX(startLayoutPosition.getX() + dx); pressed.setLayoutY(startLayoutPosition.getY() + dy); boolean changed = updateNodesUnderMouse(event.getSceneX(), event.getSceneY()); if (changed) { System.out.println( "targets: " + Arrays.asList(nodesUnderMouse.toArray())); } } } protected void onMousePress(MouseEvent event) { pressed = (Node) event.getTarget(); System.out.println("press " + pressed); startMousePosition = new Point2D(event.getSceneX(), event.getSceneY()); startLayoutPosition = new Point2D(pressed.getLayoutX(), pressed.getLayoutY()); // add effect pressed.setEffect(new Bloom(0)); IAnchor ifxAnchor = anchors.get(pressed); if (ifxAnchor != null) { Set<AnchorKey> keys = ifxAnchor.positionsUnmodifiableProperty().keySet(); for (AnchorKey key : keys) { key.getAnchored().setEffect(null); } } } protected void onSceneSizeChange(double width, double height) { // clear visuals anchors.clear(); contentLayer.getChildren().clear(); handleLayer.getChildren().clear(); // generate contents int count = 64; for (int i = 0; i < count; i++) { handleLayer.getChildren().add(draggable(generate(width, height))); } // generate random curves between for (int i = 0; i < count; i++) { Node n = handleLayer.getChildren() .get((int) (Math.random() * count / 2)); Node m = null; while (m == null || m == n) { m = handleLayer.getChildren() .get((int) (Math.random() * count / 2)); } Connection connection = new Connection(); IAnchor an, am; if (anchors.containsKey(n)) { an = anchors.get(n); } else { an = new DynamicAnchor(n); anchors.put(n, an); } if (anchors.containsKey(m)) { am = anchors.get(m); } else { am = new DynamicAnchor(n); anchors.put(m, am); } connection.setStartAnchor(an); connection.setEndAnchor(am); connection.setEffect(new BoxBlur()); contentLayer.getChildren().add(connection); } } public Set<Node> pickNodes(double sceneX, double sceneY, Node root) { Set<Node> picked = new HashSet<>(); // start with given root node Queue<Node> nodes = new LinkedList<>(); nodes.add(root); while (!nodes.isEmpty()) { Node current = nodes.remove(); if (current.contains(current.sceneToLocal(sceneX, sceneY))) { picked.add(current); // test all children, too if (current instanceof Parent) { nodes.addAll(((Parent) current).getChildrenUnmodifiable()); } } } return picked; } private boolean updateNodesUnderMouse(double sceneX, double sceneY) { boolean changed = false; Set<Node> picked = pickNodes(sceneX, sceneY, stackPane); // update entered nodes for (Node n : picked) { if (!nodesUnderMouse.contains(n)) { nodesUnderMouse.add(n); changed = true; } } // update exited nodes List<Node> toRemove = new LinkedList<>(); for (Node n : nodesUnderMouse) { if (!picked.contains(n)) { toRemove.add(n); } } if (!toRemove.isEmpty()) { changed = true; } for (Node n : toRemove) { nodesUnderMouse.remove(n); } return changed; } }