The JavaFX MouseEvent is only delivered to the top-most node which is not mouse transparent. I'm looking for a way to deliver a MouseEvent to multiple nodes (mouse translucent, maybe?).
In the example below, I have 2 partially overlapping circles. The top circle listens to MOUSE_CLICKED events to change its color. The bottom circle receives MOUSE_ENTERED and MOUSE_EXITED to update its hover property, changing its color while the mouse is over it.
When the top circle is mouse transparent, the bottom circle behaves as desired, but the top circle no longer receives MOUSE_CLICKED events. If the top circle is not mouse transparent, then the bottom circle sees MOUSE_EXITED when the mouse passes over the top circle, even if the mouse remains inside the bottom circle's shape.
It is possible to support both behaviors simultaneously?
public class MainApp extends Application {
private final Random RND = new Random();
#Override
public void start(Stage stage) throws Exception {
Circle bottomCircle = new Circle(150, 150, 100, Color.BLUE);
bottomCircle.fillProperty().bind(Bindings.when(bottomCircle.hoverProperty()).then(Color.AQUA).otherwise((Color.BLUE)));
Circle topCircle = new Circle(200, 100, 40, randColor());
topCircle.setOnMouseClicked((event) -> topCircle.setFill(randColor()));
CheckBox mouseTransparencyCheckBox = new CheckBox("Top Circle Mouse Transparency");
topCircle.mouseTransparentProperty().bind(mouseTransparencyCheckBox.selectedProperty());
Pane pane = new Pane();
pane.setPrefSize(300, 300);
pane.getChildren().addAll(mouseTransparencyCheckBox, bottomCircle, topCircle);
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.show();
}
private Color randColor() {
return Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
}
public static void main(String[] args) {
launch(args);
}
}
I figured it out using a little math. Catch all event's that happen on the top circle. If the event is Mouse entered, moved, or exited, see if the mouse is within the bottom circle.
import java.util.Random;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.EventType;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication349 extends Application
{
private final Random RND = new Random();
Circle bottomCircle = new Circle(150, 150, 100, Color.BLUE);
Circle topCircle = new Circle(200, 100, 40, randColor());
MouseEvent enteredBottomCircle = new MouseEvent(MouseEvent.MOUSE_ENTERED, bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), MouseButton.NONE, 1, true, true, true, true, true, true, true, true, true, true, null);
MouseEvent exitedBottomCircle = new MouseEvent(MouseEvent.MOUSE_EXITED, bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), MouseButton.NONE, 1, true, true, true, true, true, true, true, true, true, true, null);
#Override
public void start(Stage stage) throws Exception
{
bottomCircle.fillProperty().bind(Bindings.when(bottomCircle.hoverProperty()).then(Color.AQUA).otherwise((Color.BLUE)));
topCircle.addEventHandler(EventType.ROOT, (event) -> {
System.out.println(event.getEventType());
if (event.getEventType() == MouseEvent.MOUSE_ENTERED || event.getEventType() == MouseEvent.MOUSE_MOVED || event.getEventType() == MouseEvent.MOUSE_EXITED) {
MouseEvent event1 = (MouseEvent) event;
if (Math.sqrt(Math.pow((event1.getSceneX() - bottomCircle.getCenterX()), 2) + Math.pow((event1.getSceneY() - bottomCircle.getCenterY()), 2)) < bottomCircle.getRadius()) {
System.out.println("entered bottom circle");
bottomCircle.fireEvent(enteredBottomCircle);
}
else {
System.out.println("exited bottom circle");
bottomCircle.fireEvent(exitedBottomCircle);
}
}
});
topCircle.setOnMouseClicked((event) -> topCircle.setFill(randColor()));
CheckBox mouseTransparencyCheckBox = new CheckBox("Top Circle Mouse Transparency");
topCircle.mouseTransparentProperty().bind(mouseTransparencyCheckBox.selectedProperty());
Pane pane = new Pane();
pane.setPrefSize(300, 300);
pane.getChildren().addAll(mouseTransparencyCheckBox, bottomCircle, topCircle);
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.show();
}
private Color randColor()
{
return Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
}
public static void main(String[] args)
{
launch(args);
}
}
Related
I have this sample:
package bit.fxtest2;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class TransformTest2 extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("TransformTest2");
var bp = new BorderPane();
bp.setCenter(new DragPane());
var scene = new Scene(bp, 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
private static class DragPane extends Pane {
ObjectProperty<Transform> xform = new SimpleObjectProperty<>(new Translate(0, 0));
public DragPane() {
ObjectProperty<Point2D> mouseDown = new SimpleObjectProperty<>();
setOnMousePressed(e -> {
var mousePress = new Point2D(e.getX(), e.getY());
mouseDown.set(mousePress);
});
setOnMouseDragged(e -> {
var dragPoint = new Point2D(e.getX(), e.getY());
var delta = dragPoint.subtract(mouseDown.get());
var t = new Translate(delta.getX(), delta.getY());
xform.set(xform.get().createConcatenation(t));
mouseDown.set(dragPoint);
System.out.println("mp = " + mouseDown);
updateTransform();
});
populate();
updateTransform();
}
private void populate() {
ObservableList<Node> children = getChildren();
children.clear();
children.add(new Line(0, 0, 200, 0));
children.add(new Line(200, 0, 200, 200));
children.add(new Line(200, 200, 0, 200));
children.add(new Line(0, 200, 0, 0));
}
private void updateTransform() {
ObservableList<Transform> transforms = getTransforms();
transforms.clear();
transforms.add(xform.get());
}
}
}
If you run the code, two things happen.
First, as you start dragging, the box drags, but it starts getting very jerky, and bounces back and forth. If you print out the mouse motions they move back and forth.
Second, after you've dragged the box, say, down and to the right, you'll notice that you can no longer drag it in the upper left area of the window.
This is because the Translate is affecting the Pane itself, not necessarily the contents of the Pane. Since the OnMouse handlers are on the Pane itself, and the Pane is no longer in the upper left area, no handlers are called.
So, two questions.
First, why the jerky behavior?
Second, how can I apply Transforms (not just translate) to the children of a pane, and not the pane itself?
The answer to the first question (the jerkiness) is that it's because your calculations for the transform are incorrect.
When the dragging is processed, the pane is translated by the amount that was dragged. This leaves the coordinates of the mouse relative to the pane as being the same as they were when the mouse was first pressed.
For example, suppose you click on the pane at (100,100), so mouseDown contains the value (100,100). You then drag it, so suppose when the drag event is processed the mouse has moved to (102,101) in the pane's coordinate system. Then delta will be (2,1), so the pane will be translated by (an additional) (2,1), after which the mouse will again be over the point (100,100) in the pane's coordinate system.
Therefore, the correct thing to do here is not to change the value of mouseDown.
Simply removing the line
mouseDown.set(dragPoint);
fixes that issue.
For the second issue: As long as the user starts the drag inside the actual pane, then it all works fine; this seems to be the natural thing to do.
But if you really want to be able to drag from anywhere in the window, you can place the nodes to be dragged in a Group and apply the translation to the group. Note that this time, because the Pane is not moving, you do need to update the mouseDown value:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class TransformTest2 extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("TransformTest2");
var bp = new BorderPane();
bp.setCenter(new DragPane());
var scene = new Scene(bp, 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
private static class DragPane extends Pane {
ObjectProperty<Transform> xform = new SimpleObjectProperty<>(new Translate(0, 0));
Group group ;
public DragPane() {
ObjectProperty<Point2D> mouseDown = new SimpleObjectProperty<>();
group = new Group();
getChildren().add(group);
setOnMousePressed(e -> {
var mousePress = new Point2D(e.getX(), e.getY());
mouseDown.set(mousePress);
});
setOnMouseDragged(e -> {
var dragPoint = new Point2D(e.getX(), e.getY());
var delta = dragPoint.subtract(mouseDown.get());
var t = new Translate(delta.getX(), delta.getY());
xform.set(xform.get().createConcatenation(t));
mouseDown.set(dragPoint);
System.out.println("mp = " + mouseDown);
updateTransform();
});
populate();
updateTransform();
}
private void populate() {
ObservableList<Node> children = group.getChildren();
children.clear();
children.add(new Line(0, 0, 200, 0));
children.add(new Line(200, 0, 200, 200));
children.add(new Line(200, 200, 0, 200));
children.add(new Line(0, 200, 0, 0));
}
private void updateTransform() {
ObservableList<Transform> transforms = group.getTransforms();
transforms.clear();
transforms.add(xform.get());
}
}
}
If you don't want the additional node, you can achieve the same effect by handling the mouse events on the scene, and update the transforms for the pane.
I have an AnchorPane which contains a clipped circle. I set a maximum height to the anchorpane, so that if the circle's y position is high, the circle won't be displayed. The problem is that when the circle goes to the lower part of the anchorpane, it increases it's height. This should not be happening.
This happens even before the clipped element reaches the lower part of the anchorpane. Once the "invisible" part of the circle reaches the lower part, it starts increasing it's height.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Init extends Application {
private AnchorPane canvasContainer;
private AnchorPane mainPane;
#Override
public void start(Stage stage) throws Exception {
canvasContainer = new AnchorPane();
mainPane = new AnchorPane();
Scene scene = new Scene(mainPane, 800, 700);
stage.setScene(scene);
canvasContainer.setPrefWidth(600.0d);
canvasContainer.setPrefHeight(500.0d);
//IGNORED
canvasContainer.setMaxHeight(canvasContainer.getPrefHeight());
canvasContainer.setLayoutX(14.0d);
canvasContainer.setLayoutY(14.0d);
canvasContainer.setStyle("-fx-border-color: black; -fx-border-width: 1 1 1 1;");
RadialGradient gradient = new RadialGradient(0, 0, 0.5, 0.5, 1, true, CycleMethod.NO_CYCLE, new Stop[] {
new Stop(0, Color.ORANGE),
new Stop(0.2, Color.YELLOW),
new Stop(0.5, Color.TRANSPARENT)
});
//I AM MODIFYING THIS VALUE
int y = 500;
Circle circleGradient = new Circle(200, y, 50);
circleGradient.setFill(gradient);
Rectangle rect = new Rectangle(200 - 50, y - 50, 1000, 50/2);
circleGradient.setClip(rect);
canvasContainer.getChildren().addAll(circleGradient);
mainPane.getChildren().add(canvasContainer);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I had the same issue when I was trying to make a circle grow so it would fill a rectangle which was a small area of my Scene. The filling animation worked great but the maxHeight and maxWidth of your canvasContainer are ignored. In my case that ended up with the area growing and destroying everything nearby.
Solution : add the circle to the mainPane and not canvasContainer
I would like to intercept mouse events on canvas only where I've drawn some shape, but in all other transparent area I would like to have behaviour like with property mouseTransparent true.
I can achieve that with ImageView, transparent areas doesn't intercept mouse events.
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Drawing Operations Test");
Pane root = new Pane();
root.setStyle("-fx-background-color: #C9E3AF");
root.setMinSize(1000, 1000);
root.setOnMouseClicked(event -> {
System.out.println("Clicked on root pane");
});
Canvas canvas1 = new Canvas(512, 512);
canvas1.getGraphicsContext2D().setFill(Color.BLACK);
canvas1.getGraphicsContext2D().fillRect(250, 250, 250, 250);
canvas1.setOnMouseClicked(event -> {
System.out.println("Clicked on canvas1");
});
canvas1.setPickOnBounds(false);
Canvas canvas2 = new Canvas(512, 512);
canvas2.getGraphicsContext2D().setFill(Color.RED);
canvas2.getGraphicsContext2D().fillRect(200, 200, 250, 250);
canvas2.setOnMouseClicked(event -> {
System.out.println("Clicked on canvas2");
});
canvas2.setPickOnBounds(false);
SnapshotParameters param1 = new SnapshotParameters();
param1.setFill(Color.TRANSPARENT);
WritableImage image1 = canvas1.snapshot(param1, new WritableImage(512, 512));
SnapshotParameters param2 = new SnapshotParameters();
param2.setFill(Color.TRANSPARENT);
WritableImage image2 = canvas2.snapshot(param2, new WritableImage(512, 512));
ImageView view1 = new ImageView(image1);
view1.setOnMouseClicked(event -> {
System.out.println("Clicked on view1");
});
view1.setPickOnBounds(false);
ImageView view2 = new ImageView(image2);
view2.setOnMouseClicked(event -> {
System.out.println("Clicked on view2");
});
view2.setPickOnBounds(false);
// ImageView test
// root.getChildren().addAll(view1, view2);
// Canvas test
root.getChildren().addAll(canvas1, canvas2);
Scene sc = new Scene(root);
primaryStage.setScene(sc);
primaryStage.setX(0);
primaryStage.setY(0);
primaryStage.show();
}
Is it even possible with Canvas?
As far as I'm concerned it is impossible to achieve using Canvas, but using Group and Shapes within gives you all the features of Canvas plus behaviour you expect.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class ShapesApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Circle circle = new Circle(100);
circle.setFill(Color.BLUE);
Group group = new Group(circle);
group.setOnMouseMoved(System.out::println);
StackPane stackPane = new StackPane(group);
stackPane.setPrefSize(400, 400);
stage.setScene(new Scene(stackPane));
stage.show();
}
}
I have a JavaFX application with a pane that contains rectangles. These rectangles can be moved by dragging the mouse.
When I drag a rectangle over another rectangle, I would like the second (background) rectangle to be highlighted. This works, see code below
private boolean moveInProgress;
private Point2D prevPos;
public void onMousePressed(MouseEvent event) {
setMouseTransparent(true);
Point2D point = new Point2D(event.getSceneX(), event.getSceneY());
if (!moveInProgress) {
moveInProgress = true;
prevPos = point;
LOG.debug("Mouse move started on location " + prevPos);
}
event.consume();
}
public void onMouseDragged(MouseEvent event) {
if (moveInProgress) {
Point2D point = new Point2D(event.getSceneX(), event.getSceneY());
this.toFront();
double[] translationVector = new double[2];
translationVector[0] = point.getX() - prevPos.getX();
translationVector[1] = point.getY() - prevPos.getY();
setTranslateX(getTranslateX() + translationVector[0]);
setTranslateY(getTranslateY() + translationVector[1]);
prevPos = point;
}
event.consume();
}
public void onMouseReleased(MouseEvent event) {
setMouseTransparent(false);
if (moveInProgress) {
moveInProgress = false;
}
event.consume();
}
public void onDragDetected(MouseEvent event) {
startFullDrag();
event.consume();
}
public void onMouseDragEntered(MouseDragEvent event) {
getStyleClass().add("drag-target");
event.consume();
}
public void onMouseDragExited(MouseDragEvent event) {
if (getStyleClass().contains("drag-target")) {
getStyleClass().remove("drag-target");
}
event.consume();
}
I would like to highlight the underlying rectangle when more than half of my dragging rectangle overlaps. In this picture, I would like to highlight the red rectangle, since the grey rectangle overlaps more than half of it.
The problem is that the MouseDragEntered and MouseDragExited events are fired based on my mouse position. When my mouse position is for example the black dot in the picture, my mouse events will only be fired when my mouse enters the red rectangle.
Can anyone give me some pointers how to highlight the red rectangle when during a drag action of the grey rectangle, more than half of it overlaps?
One approach is to have each rectangle observe the bounds of the rectangle that is being dragged. Then it's reasonably easy to do a computation using Shape.intersect (or by other means) to see if the rectangle is 50% covered by the rectangle being dragged. The tricky part here is adding the listeners to the rectangle being dragged and removing them again when the rectangle stops being dragged.
Here's a quick example. I think I have things set up a little differently from the way you have them set up, but you should be able to adapt this to your use case easily enough.
import java.util.Random;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.css.PseudoClass;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
public class DraggingHighlightRectangles extends Application {
private final Random rng = new Random();
private final ObjectProperty<Rectangle> draggingRectangle = new SimpleObjectProperty<>();
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
pane.setMinSize(600, 600);
Button newRectButton = new Button("New Rectangle");
newRectButton.setOnAction(e -> pane.getChildren().add(createRectangle()));
BorderPane.setAlignment(newRectButton, Pos.CENTER);
BorderPane.setMargin(newRectButton, new Insets(5));
BorderPane root = new BorderPane(pane);
root.setBottom(newRectButton);
Scene scene = new Scene(root);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private Rectangle createRectangle() {
Rectangle rect = new Rectangle(rng.nextInt(400)+100, rng.nextInt(500)+50, 100, 50);
rect.setFill(randomColor());
rect.getStyleClass().add("rect");
ChangeListener<Bounds> boundsListener = (obs, oldBounds, newBounds) -> {
double myArea = rect.getWidth() * rect.getHeight() ;
Shape intersection = Shape.intersect(draggingRectangle.get(), rect);
Bounds intersectionBounds = intersection.getBoundsInLocal();
double intersectionArea = intersectionBounds.getWidth() * intersectionBounds.getHeight() ;
rect.pseudoClassStateChanged(PseudoClass.getPseudoClass("highlight"), intersectionArea >= 0.5 * myArea);
};
draggingRectangle.addListener((obs, oldRect, newRect) -> {
if (oldRect != null) {
oldRect.boundsInLocalProperty().removeListener(boundsListener);
}
if (newRect != null && newRect != rect) {
newRect.boundsInLocalProperty().addListener(boundsListener);
}
rect.pseudoClassStateChanged(PseudoClass.getPseudoClass("highlight"), false);
});
class MouseLocation { double x, y ; }
MouseLocation mouseLocation = new MouseLocation();
rect.setOnMousePressed(e -> {
draggingRectangle.set(rect);
rect.toFront();
mouseLocation.x = e.getX() ;
mouseLocation.y = e.getY() ;
});
rect.setOnMouseDragged(e -> {
rect.setX(rect.getX() + e.getX() - mouseLocation.x);
rect.setY(rect.getY() + e.getY() - mouseLocation.y);
mouseLocation.x = e.getX() ;
mouseLocation.y = e.getY() ;
});
rect.setOnMouseReleased(e -> draggingRectangle.set(null));
return rect ;
}
private Color randomColor() {
return Color.rgb(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
}
public static void main(String[] args) {
launch(args);
}
}
My stylesheet, style.css, just contains
.rect:highlight {
-fx-fill: yellow ;
}
Scenario : A Stage contains a Scene.The Scene contains a StackPane. The height and width of the StackPane and Scene are bind together. The StackPane contains a Shape which is a union of Rectangle and Shape.
Issue - I am facing issue while binding a Shape class to the height of StackPane. How to bind a particular part of Shape class or the complete Shape class in my example?
Requirement - I have 2 requirements.
When I maximize the stage, the StackPane gets increased since the
height and width are bind to Scene but the Shape doesnt increase. I
need both the Shape's(smallShape and bigRectangle) to increase
in terms of height only.
When I maximize the stage the StackPane should increase as well as only the bigger rectangle height should increase but not the other small rectangle i.e bigRectangle height should increase only.
Below is my code snippet
package application;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.RectangleBuilder;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
public class BindingShape extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane stackPane = new StackPane();
stackPane.setPrefHeight(200);
stackPane.setPrefWidth(200);
stackPane.setStyle("-fx-background-color: BEIGE");
Shape smallShape = RectangleBuilder.create()
.x(0)
.y(3)
.arcWidth(6)
.arcHeight(6)
.width(50) // allow overlap for union of tab and content rectangle
.height(50)
.build();
Rectangle bigRectangle = RectangleBuilder.create()
.x(25)
.y(0)
.arcWidth(10)
.arcHeight(10)
.width(100)
.height(100)
.build();
Shape unionShape = Shape.union(smallShape, bigRectangle);
unionShape.setFill(Color.rgb(0, 0, 0, .50));
unionShape.setStroke(Color.BLUE);
Group shapeGroup = new Group();
shapeGroup.getChildren().add(unionShape);
stackPane.getChildren().add(shapeGroup);
Group paneGroup = new Group();
paneGroup.getChildren().add(stackPane);
Scene scene = new Scene(paneGroup, 400, 400,Color.LIGHTSKYBLUE);
stackPane.prefHeightProperty().bind(scene.heightProperty().divide(2));
stackPane.prefWidthProperty().bind(scene.widthProperty().divide(2));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Please let me know the solution. Thanks in advance.
As the union cannot be resized in parts, I would rebuild the union if the container has been resized like in:
public class BindStackPaneToScene extends Application {
Shape union;
Shape makeShape(double w, double h) {
Rectangle smallShape = new Rectangle(0, 3, 50, 50);
smallShape.setArcHeight(6);
smallShape.setArcWidth(6);
Rectangle bigRectangle = new Rectangle(25, 3, w/2, h/2);
bigRectangle.setArcHeight(10);
bigRectangle.setArcWidth(10);
Shape unionShape = Shape.union(smallShape, bigRectangle);
unionShape.setFill(Color.rgb(0, 0, 0, .50));
unionShape.setStroke(Color.BLUE);
return unionShape;
}
#Override
public void start(Stage primaryStage) throws Exception {
StackPane stackPane = new StackPane();
stackPane.setPrefHeight(200);
stackPane.setPrefWidth(200);
stackPane.setStyle("-fx-background-color: BEIGE");
union = makeShape(200,200);
Group shapeGroup = new Group();
shapeGroup.getChildren().add(union);
stackPane.getChildren().add(shapeGroup);
stackPane.heightProperty().addListener((p, o, n) -> {
if (union != null) shapeGroup.getChildren().remove(union);
union = makeShape(stackPane.getWidth(), n.doubleValue());
shapeGroup.getChildren().add(union);
});
Group paneGroup = new Group();
paneGroup.getChildren().add(stackPane);
Scene scene = new Scene(paneGroup, 400, 400,Color.LIGHTSKYBLUE);
stackPane.prefHeightProperty().bind(scene.heightProperty().divide(1));
stackPane.prefWidthProperty().bind(scene.widthProperty().divide(1));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}