TheBeliever12
TheBeliever12

Reputation: 97

How to set draggable bounds for a polygon - JavaFX

I have a polygon that can be resized as required and dragged/moved around the scene as desired. However, my question is how can I stop it from being dragged over buttons or my treeview list? Here is my code:

public Polygon cfp(ActionEvent event) throws IOException {

    Polygon fp = new Polygon();
    ObjectProperty<Point2D> mousePosition = new SimpleObjectProperty<>();
    //Set the anchor points for the template layout
    fp.getPoints().setAll(
            350d, 50d,
            700d, 50d,
            1050d, 50d,
            1050d, 350d,
            1050d, 650d,
            700d, 650d,
            350d, 650d,
            350d, 350d

    );

    //Allow the Floor plan to be draggable around the screen
    fp.setOnMousePressed(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            mousePosition.set(new Point2D(event.getSceneX(), event.getSceneY()));
        }
    });

    fp.setOnMouseDragged(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            double deltaX = event.getSceneX() - mousePosition.get().getX();
            double deltaY = event.getSceneY() - mousePosition.get().getY();
            fp.setLayoutX(fp.getLayoutX()+deltaX);
            fp.setLayoutY(fp.getLayoutY()+deltaY);
            mousePosition.set(new Point2D(event.getSceneX(), event.getSceneY()));
        }
    });
    //Set the colour and properties of the template layout
    fp.setStroke(Color.DARKRED);
    fp.setStrokeWidth(4);
    fp.setStrokeLineCap(StrokeLineCap.ROUND);
    fp.setFill(Color.MINTCREAM);
    container.getChildren().add(fp);
    container.getChildren().addAll(createAnchors(fp, fp.getPoints()));
    return fp;
}

private ObservableList<Anchor> createAnchors(Polygon polygon, final ObservableList<Double> points) {
    ObservableList<Anchor> anchors = FXCollections.observableArrayList();
    for (int i = 0; i < points.size(); i += 2) {

        final int idx = i;
        DoubleProperty xProperty = new ListWriteDoubleProperty(points, i);
        DoubleProperty yProperty = new ListWriteDoubleProperty(points, i + 1);

        //Bind the anchors to the polygon, so if its moved so are they
        Anchor anchor = new Anchor(Color.BLACK, xProperty, yProperty);
        anchor.layoutXProperty().bindBidirectional(polygon.layoutXProperty());
        anchor.layoutYProperty().bindBidirectional(polygon.layoutYProperty());

        xProperty.addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> ov, Number oldX, Number x) {
                points.set(idx, (double) x);
            }
        });

        yProperty.addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> ov, Number oldY, Number y) {
                points.set(idx + 1, (double) y);
            }
        });
        anchors.add(anchor);
    }
    return anchors;
}

//Creating circles to mark the anchor points to help users know where to modify from
class Anchor extends Circle {
    private final DoubleProperty x, y;

    Anchor(Color color, DoubleProperty x, DoubleProperty y) {
        super(x.get(), y.get(), 5);
        setFill(color.deriveColor(1, 1, 1, 0.5));
        setStroke(color);
        setStrokeWidth(2);
        setStrokeType(StrokeType.OUTSIDE);

        this.x = x;
        this.y = y;

        x.bind(centerXProperty());
        y.bind(centerYProperty());
        enableDrag();
    }

    //Make the circle node movable with mouse drag
    private void enableDrag() {
        final Delta dragDelta = new Delta();
        setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                // record a delta distance for the drag and drop operation.
                dragDelta.x = getCenterX() - mouseEvent.getX();
                dragDelta.y = getCenterY() - mouseEvent.getY();
                getScene().setCursor(Cursor.MOVE);
            }
        });
        setOnMouseReleased(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                getScene().setCursor(Cursor.HAND);
            }
        });
        setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                double newX = mouseEvent.getX() + dragDelta.x;
                if (newX > 0 && newX < getScene().getWidth()) {
                    setCenterX(newX);
                }
                double newY = mouseEvent.getY() + dragDelta.y;
                if (newY > 0 && newY < getScene().getHeight()) {
                    setCenterY(newY);
                }
            }
        });
        setOnMouseEntered(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                if (!mouseEvent.isPrimaryButtonDown()) {
                    getScene().setCursor(Cursor.HAND);
                }
            }
        });
        setOnMouseExited(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                if (!mouseEvent.isPrimaryButtonDown()) {
                    getScene().setCursor(Cursor.DEFAULT);
                }
            }
        });
    }

    // records the x and y co-ordinates.
    private class Delta {
        double x, y;
    }

}

Here is my problem:

enter image description here

the scene where the polygon generates has an Anchor pane, the treeview is in a HBox and so are the buttons if that helps anyone.

Upvotes: 0

Views: 1009

Answers (1)

jewelsea
jewelsea

Reputation: 159291

What is Happening

Your check for the drag points for the anchor are based upon the scene dimensions rather than the dimensions of the parent container of the polygon.

How to Fix it

Change your checks to be based upon the parent container dimensions.

From:

setOnMouseDragged(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent mouseEvent) {
        double newX = mouseEvent.getX() + dragDelta.x;
        if (newX > 0 && newX < getScene().getWidth()) {
            setCenterX(newX);
        }
        double newY = mouseEvent.getY() + dragDelta.y;
        if (newY > 0 && newY < getScene().getHeight()) {
            setCenterY(newY);
        }
    }
});

To:

setOnMouseDragged(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent mouseEvent) {
        double newX = mouseEvent.getX() + dragDelta.x;
        if (newX > 0 && newX < getParent().getLayoutBounds().getWidth()) {
            setCenterX(newX);
        }
        double newY = mouseEvent.getY() + dragDelta.y;
        if (newY > 0 && newY < getParent().getLayoutBounds().getHeight()) {
            setCenterY(newY);
        }
    }
});

Ensure that your parent is a resizable parent (e.g. a Pane and not a Group), otherwise the parent won't automatically expand to fill the available area in which the polygon can be placed.

Additional Concerns

If you resize the scene so that the area in which the polygon can be rendered is smaller than the size of the polygon, then the polygon will still overflow the available bounds (as they have now shrunk smaller than the size of the polygon). There a couple of ways you could handle that situation.

  1. You could place the polygon in a ScrollPane, so that the user can scroll around if the currently viewable area gets too small. This is probably the preferred solution, but is a bit more complicated to implement well (and it is isn't really the question you asked). I won't provide example code for this at the moment.
  2. You can apply a clip to the parent container so that it does not draw outside the visible area. For example if your container Pane for the polygon is named polyPane:

    Rectangle clip = new Rectangle();
    clip.widthProperty().bind(polyPane.widthProperty());
    clip.heightProperty().bind(polyPane.heightProperty());
    polyPane.setClip(clip);
    

Content sourced from StackOverflow requires attribution.

Upvotes: 2

Related Questions