Created
October 3, 2012 20:00
-
-
Save miho/3829465 to your computer and use it in GitHub Desktop.
Draggable Node 02 (make JavaFX nodes draggable)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javafx.application.Application; | |
import javafx.event.ActionEvent; | |
import javafx.event.EventHandler; | |
import javafx.scene.Cursor; | |
import javafx.scene.Node; | |
import javafx.scene.Scene; | |
import javafx.scene.control.Button; | |
import javafx.scene.input.MouseEvent; | |
import javafx.scene.input.ScrollEvent; | |
import javafx.scene.layout.Pane; | |
import javafx.scene.layout.StackPane; | |
import javafx.scene.paint.Color; | |
import javafx.scene.transform.Scale; | |
import javafx.stage.Stage; | |
/** | |
* Draggable node sample. | |
* | |
* @author Michael Hoffer <[email protected]> | |
*/ | |
public class DraggableNode02 extends Application { | |
public static final String CSS_STYLE = | |
" -fx-glass-color: rgba(85, 132, 160, 0.9);\n" | |
+ " -fx-alignment: center;\n" | |
+ " -fx-font-size: 20;\n" | |
+ " -fx-background-color: linear-gradient(to bottom, derive(-fx-glass-color, 50%), -fx-glass-color);\n" | |
+ " -fx-border-color: derive(-fx-glass-color, -60%);\n" | |
+ " -fx-border-width: 2;\n" | |
+ " -fx-background-insets: 1;\n" | |
+ " -fx-border-radius: 3;\n" | |
+ " -fx-background-radius: 3;\n"; | |
@Override | |
public void start(Stage primaryStage) { | |
// we use a default pane without layout such as HBox, VBox etc. | |
final Pane root = new Pane(); | |
final Scene scene = new Scene(root, 800, 600, Color.rgb(160, 160, 160)); | |
final int numNodes = 6; // number of nodes to add | |
final double spacing = 30; // spacing between nodes | |
// add numNodes instances of DraggableNode to the root pane | |
for (int i = 0; i < numNodes; i++) { | |
DraggableNode node = new DraggableNode(); | |
node.setPrefSize(98, 80); | |
// define the style via css | |
node.setStyle(CSS_STYLE); | |
// position the node | |
node.setLayoutX(spacing * (i + 1) + node.getPrefWidth() * i); | |
node.setLayoutY(spacing); | |
// add the node to the root pane | |
root.getChildren().add(node); | |
} | |
// finally, show the stage | |
primaryStage.setTitle("Draggable Node 02"); | |
primaryStage.setScene(scene); | |
primaryStage.show(); | |
} | |
/** | |
* The main() method is ignored in correctly deployed JavaFX application. | |
* main() serves only as fallback in case the application can not be | |
* launched through deployment artifacts, e.g., in IDEs with limited FX | |
* support. NetBeans ignores main(). | |
* | |
* @param args the command line arguments | |
*/ | |
public static void main(String[] args) { | |
launch(args); | |
} | |
} | |
/** | |
* Simple draggable node. | |
* | |
* Dragging code based on | |
* {@link http://blog.ngopal.com.np/2011/06/09/draggable-node-in-javafx-2-0/} | |
* | |
* @author Michael Hoffer <[email protected]> | |
*/ | |
class DraggableNode extends Pane { | |
// node position | |
private double x = 0; | |
private double y = 0; | |
// mouse position | |
private double mousex = 0; | |
private double mousey = 0; | |
private Node view; | |
private boolean dragging = false; | |
private boolean moveToFront = true; | |
private Scale scaleTransform; | |
private boolean zoomable = true; | |
private double minScale = 0.1; | |
private double maxScale = 10; | |
private double scaleIncrement = 0.001; | |
private ResizeMode resizeMode; | |
private boolean RESIZE_TOP; | |
private boolean RESIZE_LEFT; | |
private boolean RESIZE_BOTTOM; | |
private boolean RESIZE_RIGHT; | |
public DraggableNode() { | |
init(); | |
} | |
public DraggableNode(Node view) { | |
this.view = view; | |
getChildren().add(view); | |
init(); | |
} | |
private void init() { | |
scaleTransform = new Scale(1, 1); | |
scaleTransform.setPivotX(0); | |
scaleTransform.setPivotY(0); | |
scaleTransform.setPivotZ(0); | |
getTransforms().add(scaleTransform); | |
onMousePressedProperty().set(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
final Node n = DraggableNode.this; | |
final double parentScaleX = n.getParent().localToSceneTransformProperty().getValue().getMxx(); | |
final double parentScaleY = n.getParent().localToSceneTransformProperty().getValue().getMyy(); | |
final double scaleX = n.localToSceneTransformProperty().getValue().getMxx(); | |
final double scaleY = n.localToSceneTransformProperty().getValue().getMyy(); | |
// record the current mouse X and Y position on Node | |
mousex = event.getSceneX(); | |
mousey = event.getSceneY(); | |
x = n.getLayoutX() * parentScaleX; | |
y = n.getLayoutY() * parentScaleY; | |
if (isMoveToFront()) { | |
toFront(); | |
} | |
} | |
}); | |
//Event Listener for MouseDragged | |
onMouseDraggedProperty().set(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
final Node n = DraggableNode.this; | |
final double parentScaleX = n.getParent().localToSceneTransformProperty().getValue().getMxx(); | |
final double parentScaleY = n.getParent().localToSceneTransformProperty().getValue().getMyy(); | |
final double scaleX = n.localToSceneTransformProperty().getValue().getMxx(); | |
final double scaleY = n.localToSceneTransformProperty().getValue().getMyy(); | |
// Get the exact moved X and Y | |
double offsetX = event.getSceneX() - mousex; | |
double offsetY = event.getSceneY() - mousey; | |
if (resizeMode == ResizeMode.NONE) { | |
x += offsetX; | |
y += offsetY; | |
double scaledX = x * 1 / parentScaleX; | |
double scaledY = y * 1 / parentScaleY; | |
n.setLayoutX(scaledX); | |
n.setLayoutY(scaledY); | |
dragging = true; | |
} else { | |
double width = n.getBoundsInLocal().getMaxX() | |
- n.getBoundsInLocal().getMinX(); | |
double height = n.getBoundsInLocal().getMaxY() | |
- n.getBoundsInLocal().getMinY(); | |
if (RESIZE_TOP) { | |
double newHeight = | |
getBoundsInLocal().getHeight() | |
- offsetY / scaleY | |
- getInsets().getTop(); | |
y += offsetY; | |
double scaledY = y / parentScaleY; | |
setLayoutY(scaledY); | |
setPrefHeight(newHeight); | |
} | |
if (RESIZE_LEFT) { | |
double newWidth = | |
getBoundsInLocal().getWidth() | |
- offsetX / scaleX | |
- getInsets().getLeft(); | |
x += offsetX; | |
double scaledX = x / parentScaleX; | |
setLayoutX(scaledX); | |
setPrefWidth(newWidth); | |
} | |
if (RESIZE_BOTTOM) { | |
double newHeight = | |
getBoundsInLocal().getHeight() | |
+ offsetY / scaleY | |
- getInsets().getBottom(); | |
setPrefHeight(newHeight); | |
} | |
if (RESIZE_RIGHT) { | |
double newWidth = | |
getBoundsInLocal().getWidth() | |
+ offsetX / scaleX | |
- getInsets().getRight(); | |
setPrefWidth(newWidth); | |
} | |
} | |
// again set current Mouse x AND y position | |
mousex = event.getSceneX(); | |
mousey = event.getSceneY(); | |
event.consume(); | |
} | |
}); | |
onMouseClickedProperty().set(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
dragging = false; | |
} | |
}); | |
onMouseMovedProperty().set(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent t) { | |
final Node n = DraggableNode.this; | |
final double parentScaleX = n.getParent().localToSceneTransformProperty().getValue().getMxx(); | |
final double parentScaleY = n.getParent().localToSceneTransformProperty().getValue().getMyy(); | |
final double scaleX = n.localToSceneTransformProperty().getValue().getMxx(); | |
final double scaleY = n.localToSceneTransformProperty().getValue().getMyy(); | |
final double border = 10; | |
double diffMinX = Math.abs(n.getBoundsInLocal().getMinX() - t.getX()); | |
double diffMinY = Math.abs(n.getBoundsInLocal().getMinY() - t.getY()); | |
double diffMaxX = Math.abs(n.getBoundsInLocal().getMaxX() - t.getX()); | |
double diffMaxY = Math.abs(n.getBoundsInLocal().getMaxY() - t.getY()); | |
boolean left = diffMinX * scaleX < border; | |
boolean top = diffMinY * scaleY < border; | |
boolean right = diffMaxX * scaleX < border; | |
boolean bottom = diffMaxY * scaleY < border; | |
RESIZE_TOP = false; | |
RESIZE_LEFT = false; | |
RESIZE_BOTTOM = false; | |
RESIZE_RIGHT = false; | |
if (left && !top && !bottom) { | |
n.setCursor(Cursor.W_RESIZE); | |
resizeMode = ResizeMode.LEFT; | |
RESIZE_LEFT = true; | |
} else if (left && top && !bottom) { | |
n.setCursor(Cursor.NW_RESIZE); | |
resizeMode = ResizeMode.TOP_LEFT; | |
RESIZE_LEFT = true; | |
RESIZE_TOP = true; | |
} else if (left && !top && bottom) { | |
n.setCursor(Cursor.SW_RESIZE); | |
resizeMode = ResizeMode.BOTTOM_LEFT; | |
RESIZE_LEFT = true; | |
RESIZE_BOTTOM = true; | |
} else if (right && !top && !bottom) { | |
n.setCursor(Cursor.E_RESIZE); | |
resizeMode = ResizeMode.RIGHT; | |
RESIZE_RIGHT = true; | |
} else if (right && top && !bottom) { | |
n.setCursor(Cursor.NE_RESIZE); | |
resizeMode = ResizeMode.TOP_RIGHT; | |
RESIZE_RIGHT = true; | |
RESIZE_TOP = true; | |
} else if (right && !top && bottom) { | |
n.setCursor(Cursor.SE_RESIZE); | |
resizeMode = ResizeMode.BOTTOM_RIGHT; | |
RESIZE_RIGHT = true; | |
RESIZE_BOTTOM = true; | |
} else if (top && !left && !right) { | |
n.setCursor(Cursor.N_RESIZE); | |
resizeMode = ResizeMode.TOP; | |
RESIZE_TOP = true; | |
} else if (bottom && !left && !right) { | |
n.setCursor(Cursor.S_RESIZE); | |
resizeMode = ResizeMode.BOTTOM; | |
RESIZE_BOTTOM = true; | |
} else { | |
n.setCursor(Cursor.DEFAULT); | |
resizeMode = ResizeMode.NONE; | |
} | |
} | |
}); | |
setOnScroll(new EventHandler<ScrollEvent>() { | |
@Override | |
public void handle(ScrollEvent event) { | |
if (!isZoomable()) { | |
return; | |
} | |
double scaleValue = | |
scaleTransform.getY() + event.getDeltaY() * getScaleIncrement(); | |
scaleValue = Math.max(scaleValue, getMinScale()); | |
scaleValue = Math.min(scaleValue, getMaxScale()); | |
scaleTransform.setX(scaleValue); | |
scaleTransform.setY(scaleValue); | |
scaleTransform.setPivotX(0); | |
scaleTransform.setPivotX(0); | |
scaleTransform.setPivotZ(0); | |
// setScaleX(scaleValue); | |
// setScaleY(scaleValue); | |
event.consume(); | |
} | |
}); | |
} | |
/** | |
* @return the zoomable | |
*/ | |
public boolean isZoomable() { | |
return zoomable; | |
} | |
/** | |
* @param zoomable the zoomable to set | |
*/ | |
public void setZoomable(boolean zoomable) { | |
this.zoomable = zoomable; | |
} | |
/** | |
* @return the dragging | |
*/ | |
protected boolean isDragging() { | |
return dragging; | |
} | |
/** | |
* @return the view | |
*/ | |
public Node getView() { | |
return view; | |
} | |
/** | |
* @param moveToFront the moveToFront to set | |
*/ | |
public void setMoveToFront(boolean moveToFront) { | |
this.moveToFront = moveToFront; | |
} | |
/** | |
* @return the moveToFront | |
*/ | |
public boolean isMoveToFront() { | |
return moveToFront; | |
} | |
public void removeNode(Node n) { | |
getChildren().remove(n); | |
} | |
/** | |
* @return the minScale | |
*/ | |
public double getMinScale() { | |
return minScale; | |
} | |
/** | |
* @param minScale the minScale to set | |
*/ | |
public void setMinScale(double minScale) { | |
this.minScale = minScale; | |
} | |
/** | |
* @return the maxScale | |
*/ | |
public double getMaxScale() { | |
return maxScale; | |
} | |
/** | |
* @param maxScale the maxScale to set | |
*/ | |
public void setMaxScale(double maxScale) { | |
this.maxScale = maxScale; | |
} | |
/** | |
* @return the scaleIncrement | |
*/ | |
public double getScaleIncrement() { | |
return scaleIncrement; | |
} | |
/** | |
* @param scaleIncrement the scaleIncrement to set | |
*/ | |
public void setScaleIncrement(double scaleIncrement) { | |
this.scaleIncrement = scaleIncrement; | |
} | |
} | |
enum ResizeMode { | |
NONE, | |
TOP, | |
LEFT, | |
BOTTOM, | |
RIGHT, | |
TOP_LEFT, | |
TOP_RIGHT, | |
BOTTOM_LEFT, | |
BOTTOM_RIGHT | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment