I have nodes that I can move, these nodes are placed on the Pane, which is on the ScrollPane.When I drag a node outside of the viewportBounds
of the scrollPane, the vvalue
should change so that the node is again within those bounds. To solve it I tried to use answers from this question.
My problem is that after the node is again within the boundaries of the viewportBounds
, the cursor moves relative to the node, if I want to continue moving the node outside the viewport, after several iterations the cursor will move so much that it will be outside the entire application window and will rest against the screen boundaries. How do I maintain position of cursor on the node?
If you want to test code, keep in mind that the restructuring of the viewport
boundaries occurs only when you move nodes along the Y axis.
public class NewFXMain extends Application {
@Override
public void start(Stage primaryStage) {
AnchorPane root = new AnchorPane();
ScrollPane scrollPane = new ScrollPane(root);
root.setPrefSize(5000,5000);
Scene scene = new Scene(scrollPane, 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(scrollPane);
node.setPrefSize(98, 80);
// define the style via css
node.setStyle(
"-fx-background-color: #334488; "
+ "-fx-text-fill: black; "
+ "-fx-border-color: black;");
node.setLayoutX(spacing * (i + 1) + node.getPrefWidth() * i);// position the node
node.setLayoutY(spacing);
root.getChildren().add(node); // add the node to the root pane
}
// finally, show the stage
primaryStage.setTitle("Draggable Node 01");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class DraggableNode extends Pane {
private double x = 0;
private double y = 0;
private double mousex = 0;
private double mousey = 0;
private Node view;
private boolean dragging = false;
private boolean moveToFront = true;
private void ensureVisible(ScrollPane scrollPane) {
Bounds viewport = scrollPane.getViewportBounds();
double contentHeight = scrollPane.getContent().localToScene(scrollPane.getContent().getBoundsInLocal()).getHeight();
double nodeMinY = this.localToScene(this.getBoundsInLocal()).getMinY();
double nodeMaxY = this.localToScene(this.getBoundsInLocal()).getCenterY();
double vValueDelta = 0;
double vValueCurrent = scrollPane.getVvalue();
if (nodeMaxY < 0) {
// currently located above (remember, top left is (0,0))
vValueDelta = (nodeMinY) / contentHeight;
System.out.println("FIRST CASE DELTA: " + vValueDelta);
} else if (nodeMinY > viewport.getHeight()) {
// currently located below
vValueDelta = ((nodeMinY) / contentHeight) / 5;
System.out.println("SECOND CASE DELTA: " + vValueDelta);
}
scrollPane.setVvalue(vValueCurrent + vValueDelta);
}
public DraggableNode(ScrollPane pane) {
init(pane);
}
private void init(ScrollPane scroll) {
onMousePressedProperty().set(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// record the current mouse X and Y position on Node
mousex = event.getSceneX();
mousey = event.getSceneY();
x = getLayoutX();
y = getLayoutY();
if (isMoveToFront()) {
toFront();
}
}
});
//Event Listener for MouseDragged
onMouseDraggedProperty().set(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// Get the exact moved X and Y
double offsetX = event.getSceneX() - mousex;
double offsetY = event.getSceneY() - mousey;
x += offsetX;
y += offsetY;
double scaledX = x;
double scaledY = y;
setLayoutX(scaledX);
setLayoutY(scaledY);
ensureVisible(scroll);
dragging = true;
// 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;
}
});
}
public void setMoveToFront(boolean moveToFront) {
this.moveToFront = moveToFront;
}
public boolean isMoveToFront() {
return moveToFront;
}
}