Transparent Stage should not minimized when clicked inside in Javafx - javafx

I am learning to create Screen Recording application in JavaFx. I want user to resize the rectangle to decide the screen capture area. I have made stage and scene Transparent by primaryStage.initStyle(StageStyle.TRANSPARENT); and scene.setFill(null); .
I am able to resize the rectangular section but the problem is When I click inside the stage, It gets minimized as it is transparent. How to solve this issue ?
I have seen this application screencast-o-matics and following the same. Please guide me on this.
Edit::
Code:
import java.awt.Toolkit;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class ScreenCaptureDemo extends Application {
Rectangle rectangle ;
double x0,y0;
public static void main(String[] args) {
launch(ScreenCaptureDemo.class);
}
#Override
public void start(final Stage primaryStage) throws Exception {
BorderPane borderPane = new BorderPane();
GridPane gridPane = new GridPane();
HBox box = new HBox();
Button button1 = new Button("button2");
Button button2 = new Button("Button3");
Button button = new Button("button");
box.getChildren().add(button);
box.getChildren().add(button1);
box.getChildren().add(button2);
rectangle = new Rectangle(500.0, 500.0);
rectangle.setStrokeWidth(2);
rectangle.setArcHeight(15.0);
rectangle.setArcWidth(15.0);
rectangle.setFill(Color.TRANSPARENT);
rectangle.setStroke(Color.RED);
rectangle.setStrokeWidth(5);
rectangle.getStrokeDashArray().addAll(3.0,13.0,3.0,7.0);
gridPane.add(rectangle, 0, 0);
gridPane.add(box, 0, 1);
borderPane.setCenter(gridPane);
Scene scene = new Scene(borderPane,Toolkit.getDefaultToolkit().getScreenSize().getWidth()-100,Toolkit.getDefaultToolkit().getScreenSize().getHeight()-100);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMousePressed(mouseHandler);
primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.TRANSPARENT);
scene.setFill(null);
rectangle.setMouseTransparent(true);
rectangle.setPickOnBounds(true);
primaryStage.show();
}
void setScaleRect(double sX, double sY){
rectangle.setHeight(sY);
rectangle.setWidth(sX);
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
double heightLowerLimit = rectangle.getHeight()-500;
double heightUpperLimit = rectangle.getHeight()+500;
double widthLowerLimit = rectangle.getWidth()-500;
double widthUpperLimit = rectangle.getWidth()+500;
if ((mouseEvent.getY() >heightLowerLimit && mouseEvent.getY() < heightUpperLimit) &&
(mouseEvent.getX() >widthLowerLimit && mouseEvent.getX() < widthUpperLimit)
) {
double scaleX = mouseEvent.getX();
double scaleY = mouseEvent.getY();
setScaleRect(scaleX, scaleY);
} else if ((mouseEvent.getY() >heightLowerLimit && mouseEvent.getY() < heightUpperLimit)
&& (mouseEvent.getX() <widthLowerLimit && mouseEvent.getX() > widthUpperLimit)) {
double scaleY = mouseEvent.getY();
double scaleX=rectangle.getWidth();
setScaleRect(scaleX, scaleY);
} else if (mouseEvent.getY() != rectangle.getHeight()
&& mouseEvent.getX() == rectangle.getWidth()) {
double scaleX = mouseEvent.getX();
double scaleY=rectangle.getHeight();
setScaleRect(scaleX, scaleY);
}
}
}
};
}
Thank you in advance

Fill the rectangle as
rectangle.setFill(Color.web("blue", 0.1));
// or more transparent
rectangle.setFill(Color.web("gray", 0.01));

Related

Add FreeHand functionality in JavaFX

Actually I am making an application which allows user to crop an image an then gives functionality of free-hand drawing and to also to draw different shapes. I have achieved functionality of cropping and drawing shapes like line etc. But I am facing some problems in free-hand drawing.
I have added my image on "HBox" and cropped that image through "Rectangle" in JavaFX class which is added on "Group". And all these are added on "Pane" class. Now for free-hand drawing, I am using "Canvas". Where to add canvas, whether on "HBox" or "Group" or "Pane" class. And Canvas is only initialized on clicking pencil button. I have added single functions for Mouse-Events and applied if-checks for different functionalities in those functions.
basically how to draw pencil on image or how to add lineTo on group.
Can someone please help me in solving my problem??
`
package application;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.imageio.ImageIO;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.Rectangle;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class Main extends Application {
int dragStatus = 0;
double startingPointX;
double startingPointY;
double currentEndingPointX;
double currentEndingPointY;
BufferedImage bufferedImage;
Robot robot;
ImageView imageView;
Button pencilBtn;
Image image;
Pane rootPane;
HBox pictureRegion;
Scene scene;
Rectangle croppedArea;
Canvas canvas;
GraphicsContext gc;
WritableImage writableImage;
Group group = new Group();
Line line;
LineTo lineTo;
String shape;
Boolean canvasAdded;
#Override
public void start(Stage primaryStage) {
try {
int sleepTime = 120;
Thread.sleep(sleepTime);
pencilBtn = createButton("save.png");
pictureRegion = new HBox();
rootPane = new Pane();
scene = new Scene(rootPane);
robot = new Robot();
java.awt.Rectangle capture = new java.awt.Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
bufferedImage = robot.createScreenCapture(capture);
image = ConvertBufferedToJavaFXImage.convertToFxImage(bufferedImage);
pencilBtn.setOnAction(e -> geometrySelection("pencil"));
croppedArea = new Rectangle();
if(canvas != null) {
canvas.setOnMousePressed(e -> onMousePressed(e));
canvas.setOnMousePressed(e -> onMouseDragged(e));
}
imageView = new ImageView(image);
scene.setOnMouseEntered(e -> onMouseEntered(e, "scene"));
scene.setOnMousePressed(e -> onMousePressed(e));
scene.setOnMouseReleased(e -> onMouseReleased(e));
scene.setOnMouseDragged(e -> onMouseDragged(e));
pictureRegion.getChildren().add(imageView);
rootPane.getChildren().add(pictureRegion);
rootPane.getChildren().add(group);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setMaximized(true);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public void onMouseEntered(MouseEvent ev, String nodeType) {
if (nodeType == "scene") {
scene.setCursor(Cursor.CROSSHAIR);
} else if (nodeType == "croppedArea") {
croppedArea.setCursor(Cursor.TEXT);
//croppedArea.setOnMousePressed(e -> onMousePressed(e));
} else if (nodeType == "btn") {
pencilBtn.setCursor(Cursor.HAND);
}
}
public void onMousePressed(MouseEvent event) {
if ((startingPointX < event.getX() && event.getX() < currentEndingPointX)
&& (startingPointY < event.getY() && event.getY() < currentEndingPointY)) {
if(shape == "pencil") {
lineTo = new LineTo();
System.out.println("lineTo1");
gc.beginPath();
gc.lineTo(event.getX(), event.getY());
gc.stroke();
}
} else {
shape = "croppedArea";
rootPane.getChildren().remove(canvas);
group.getChildren().clear();
startingPointX = event.getX();
startingPointY = event.getY();
group.getChildren().add(croppedArea);
}
}
public void onMouseDragged(MouseEvent event) {
dragStatus = 1;
if (dragStatus == 1) {
// if((startingPointX < event.getX() && event.getX() < currentEndingPointX)
// && (startingPointY < event.getY() && event.getY() < currentEndingPointY)) {
if(shape == "pencil") {
System.out.println("lineTo1");
gc.lineTo(event.getX(), event.getY());
gc.stroke();
}
else {
currentEndingPointX = event.getX();
currentEndingPointY = event.getY();
rootPane.getChildren().remove(pencilBtn);
croppedArea.setFill(Color.TRANSPARENT);
croppedArea.setStroke(Color.BLACK);
croppedArea.setOnMouseEntered(e -> onMouseEntered(e, "croppedArea"));
adjustRectangleProperties(startingPointX, startingPointY, currentEndingPointX, currentEndingPointY,
croppedArea);
}
}
}
public void geometrySelection(String tempShape) {
if(tempShape == "pencil") {
shape = "pencil";
canvas = new Canvas();
gc = canvas.getGraphicsContext2D();
//gc.setFill(Color.LIGHTGRAY);
gc.setStroke(Color.BLACK);
gc.setLineWidth(5);
canvas.setLayoutX(startingPointX);
canvas.setLayoutY(startingPointY);
canvas.setWidth(croppedArea.getWidth());
canvas.setHeight(croppedArea.getHeight());
System.out.println("canvasAdded");
group.getChildren().add(canvas);
}
}
public void onMouseReleased(MouseEvent event) {
if(dragStatus == 1) {
if(shape == "croppedArea") {
if (croppedArea.getHeight() > 0 && croppedArea.getWidth() > 0) {
pencilBtn.setLayoutX(Math.max(startingPointX, currentEndingPointX) + 5);
pencilBtn.setLayoutY(Math.max(startingPointY, currentEndingPointY) - 120);
rootPane.getChildren().add(pencilBtn);
dragStatus = 0;
}
}
bufferedImage = robot.createScreenCapture(new java.awt.Rectangle((int) startingPointX, (int) startingPointY,
(int) croppedArea.getWidth(), (int) croppedArea.getHeight()));
}
}
void adjustRectangleProperties(Double startingPointX, Double startingPointY, Double currentEndingPointX,
Double currentEndingPointY, Rectangle givenRectangle) {
givenRectangle.setX(startingPointX);
givenRectangle.setY(startingPointY);
givenRectangle.setWidth(currentEndingPointX - startingPointX);
givenRectangle.setHeight(currentEndingPointY - startingPointY);
if (givenRectangle.getWidth() < 0) {
givenRectangle.setWidth(-givenRectangle.getWidth());
givenRectangle.setX(givenRectangle.getX() - givenRectangle.getWidth());
}
if (givenRectangle.getHeight() < 0) {
givenRectangle.setHeight(-givenRectangle.getHeight());
givenRectangle.setY(givenRectangle.getY() - givenRectangle.getHeight());
}
}
public Button createButton(String imageName) throws FileNotFoundException {
FileInputStream iconFile = new FileInputStream(imageName);
Image iconImage = new Image(iconFile);
Button button = new Button();
button.setGraphic(new ImageView(iconImage));
button.setMaxSize(20, 20);
button.setPadding(Insets.EMPTY);
return button;
}
public static void main(String[] args) {
launch(args);
}
}
`

Java FX nested event handler I want to block the parent handler when child handler is called

I have a fxml Mouse event handler method where every time I click a Pane a pop up dialog box appears on the screen asking for information. When OK is clicked on the dialog, a circle with text is added to a stack pane and the stack pane is then added to the pane where the user clicked.
However I am trying to implement a event handler where I can move the stack pane around by dragging the stack pane with the mouse. My event handler works but every time I finish dragging the stack pane the pop up dialog pops up. I do not want the pop up dialog to pop up when moving the circle how would I change my code to do this is there a way of blocking the pop up handler?
Thank you.
My Handler method:
#FXML
private void handleAddVertex(MouseEvent event) {
boolean okClicked = main.showAddVertexPopUp(this);
if(okClicked) {
String vertexText = "";
if(getSelectedDataChoice().equals("Integer")) {
vertexText = dataModel.getListOfIntVertices().get(dataModel.getListOfIntVertices().size() - 1).toString();
}else if(getSelectedDataChoice().equals("Double")){
vertexText = dataModel.getListOfDoubleVertices().get(dataModel.getListOfDoubleVertices().size() - 1).toString();
}else {
vertexText = dataModel.getListOfStringVertices().get(dataModel.getListOfStringVertices().size() - 1).toString();
}
EventHandler<MouseEvent> circleOnMousePressedEventHandler =
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
orgTranslateX = ((StackPane)(t.getSource())).getTranslateX();
orgTranslateY = ((StackPane)(t.getSource())).getTranslateY();
}
};
EventHandler<MouseEvent> circleOnMouseDraggedEventHandler =
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
((StackPane)(t.getSource())).setTranslateX(newTranslateX);
((StackPane)(t.getSource())).setTranslateY(newTranslateY);
}
};
double x = event.getX();
double y = event.getY();
Circle vertex = new Circle(x, y, 20, Color.WHITE);
vertex.setStroke(Color.BLACK);
Text text = new Text (vertexText);
StackPane stack = new StackPane();
stack.getChildren().addAll(vertex, text);
stack.setLayoutX(x);
stack.setLayoutY(y);
stack.setOnMousePressed(circleOnMousePressedEventHandler);
stack.setOnMouseDragged(circleOnMouseDraggedEventHandler);
centerPane.getChildren().add(stack);
}
}
To stop an Event from propagating you use Event.consume().
Marks this Event as consumed. This stops its further propagation.
From your description, it appears handleAddVertex is a MOUSE_CLICKED handler. You'll have to add another EventHandler to the newly created StackPane that consumes MOUSE_CLICKED events.
stack.setOnMouseClicked(Event::consume);
This will stop the MOUSE_CLICKED event from bubbling up to the Node which has the handleAddVertex handler.
For more information on event processing in JavaFX, see JavaFX: Handling Events.
Here's a small example where you can see the difference between consuming and not consuming the event:
import java.util.function.Predicate;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
private CheckBox consumeClickEventsCheckBox;
#Override
public void start(Stage primaryStage) throws Exception {
consumeClickEventsCheckBox = new CheckBox("Consume click events");
consumeClickEventsCheckBox.setSelected(true);
HBox top = new HBox(consumeClickEventsCheckBox);
top.setPadding(new Insets(10));
top.setAlignment(Pos.CENTER);
Pane center = new Pane();
center.setBorder(new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, null, null)));
center.setOnMouseClicked(this::handleAddCircle);
Rectangle clip = new Rectangle();
clip.widthProperty().bind(center.widthProperty());
clip.heightProperty().bind(center.heightProperty());
center.setClip(clip);
BorderPane root = new BorderPane(center);
root.setTop(top);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private void handleAddCircle(MouseEvent event) {
event.consume();
Alert confirm = new Alert(AlertType.CONFIRMATION);
confirm.initOwner(((Node) event.getSource()).getScene().getWindow());
confirm.setHeaderText(null);
confirm.setContentText("Do you want to add a circle here?");
if (confirm.showAndWait().filter(Predicate.isEqual(ButtonType.OK)).isPresent()) {
Circle circle = new Circle(event.getX(), event.getY(), 25);
circle.setOnMousePressed(this::handleCirclePressed);
circle.setOnMouseDragged(this::handleCircleDragged);
circle.setOnMouseClicked(this::handleCircleClicked);
((Pane) event.getSource()).getChildren().add(circle);
}
}
private Point2D origin;
private void handleCirclePressed(MouseEvent event) {
event.consume();
origin = new Point2D(event.getX(), event.getY());
}
private void handleCircleDragged(MouseEvent event) {
event.consume();
Circle circle = (Circle) event.getSource();
circle.setTranslateX(circle.getTranslateX() + event.getX() - origin.getX());
circle.setTranslateY(circle.getTranslateY() + event.getY() - origin.getY());
}
/*
* Will consume the MOUSE_CLICKED event only if the CheckBox is selected. You can test
* the behavior of consuming the event by toggling the CheckBox.
*/
private void handleCircleClicked(MouseEvent event) {
if (consumeClickEventsCheckBox.isSelected()) {
event.consume();
}
}
}

JavaFX AnimationTimer renders

My goal is to completely sync each animation frame to the monitor device vsync at perfectly 60fps. I googled online, and people suggested that i could use JavaFX AnimationTimer. I wrote a simple ball JavaFX AnimationTimer, but i could sometimes see a very noticeable jittery in the animation. Could you guys please help me?
I have attached the code here:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Box;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Main extends Application
implements EventHandler <KeyEvent>
{
final int WIDTH = 600;
final int HEIGHT = 400;
double ballRadius = 40;
double ballX = 100;
double ballY = 200;
double xSpeed = 4;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Basic JavaFX demo");
Group root = new Group();
Scene scene = new Scene(root, WIDTH, HEIGHT);
// Bouncing Ball
Circle circle = new Circle();
circle.setCenterX(ballX);
circle.setCenterY(ballY);
circle.setRadius(ballRadius);
circle.setFill(Color.BLUE);
root.getChildren().add(circle);
// need to attach KeyEvent caller to a Node of some sort.
// How about an invisible Box?
final Box keyboardNode = new Box();
keyboardNode.setFocusTraversable(true);
keyboardNode.requestFocus();
keyboardNode.setOnKeyPressed(this);
root.getChildren().add(keyboardNode);
stage.setScene(scene);
stage.show();
AnimationTimer animator = new AnimationTimer(){
#Override
public void handle(long arg0) {
long startTime = System.currentTimeMillis();
// UPDATE
ballX += xSpeed;
if (ballX + ballRadius >= WIDTH)
{
ballX = WIDTH - ballRadius;
xSpeed *= -1;
} else if (ballX - ballRadius < 0) {
ballX = 0 + ballRadius;
xSpeed *= -1;
}
// RENDER
circle.setCenterX(ballX);
long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
System.out.println(elapsedTime);
}
};
animator.start();
}
#Override
public void handle(KeyEvent arg0) {
if (arg0.getCode() == KeyCode.SPACE )
{
xSpeed *= -1;
}
}
}

Circle movement upon rectangle Collision

I'm working on understanding collision detection and movement and I am having issues getting the movement down correctly. my goal is to allow a player to move around rectangles but not through them. my problem is that once the player reaches the bounds of the rectangle they are unable to backup. any help would be greatly appreciated.
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import static javafx.scene.input.KeyCode.DOWN;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Circle;
public class CircleMovement extends Application {
//create the panes to handle the game
BorderPane mainPane = new BorderPane();
GridPane infoPane = new GridPane();
Pane gameField = new Pane();
Scene scene;
//create circle/movement properties
double increment = 5.0;
double radius = 10;
double x = radius, y = radius;
//create Rectangle properties
double Rwidth = 80;
double Rheight = 20;
//create player and objects
Circle player = new Circle(x, y, radius);
Rectangle r1 = new Rectangle(0, 100, Rwidth, Rheight);//Rectangle(int x, int y, int width, int height)
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
setPanes();
gameField.getChildren().addAll(r1, player);
moveCircleOnKeyPress(scene, player);//pass to player movement method
//set the stage
stage.setScene(scene);
stage.show();
}
public void moveCircleOnKeyPress(Scene scene, Circle player) {//player movement
scene.setOnKeyPressed((KeyEvent event) -> {
switch (event.getCode()) {
case UP:
player.setCenterY(player.getCenterY() - increment);//move player
if (player.getBoundsInLocal().intersects(r1.getBoundsInLocal())) {
player.centerYProperty().setValue(r1.getY());
}
break;
case RIGHT:
player.setCenterX(player.getCenterX() + increment);
if (player.getBoundsInLocal().intersects(r1.getBoundsInLocal())) {
player.centerYProperty().setValue(r1.getY());
}
break;
case DOWN:
player.setCenterY(player.getCenterY() + increment);
if (player.getBoundsInLocal().intersects(r1.getBoundsInLocal())) {
player.centerYProperty().setValue(r1.getY());
}
break;
case LEFT:
player.setCenterX(player.getCenterX() - increment);
if (player.getBoundsInLocal().intersects(r1.getBoundsInLocal())) {
player.centerYProperty().setValue(r1.getY());
}
break;
}
});
}
public void setPanes() {
infoPane.setMaxSize(200, 200);
mainPane.setMaxSize(800, 800);
mainPane.setCenter(gameField);//place the main game inside the center
mainPane.setRight(infoPane);
mainPane.setPadding(new Insets(10, 10, 10, 10));
mainPane.setStyle("-fx-border-color: red");
gameField.setStyle("-fx-background-color: white");
gameField.setStyle("-fx-border-color: black");
gameField.setMaxSize(600, 600);
scene = new Scene(mainPane, 800, 800);
player.setFill(Color.RED);
}
}
Since you are using Shape, it's a good idea to use Shape.intersect(player, r1).getBoundsInLocal().getWidth() != -1 to detect intersection. Also, instead of using player.centerYProperty().setValue(...);, just use player.setCenterY(...). You had a couple more problems, but the most important one is the idea of what to do once an intersection is detected. Once an intersection is detected you should go back to the distance just before the detection(only if you are moving in small enough steps).
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import static javafx.scene.input.KeyCode.DOWN;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
public class CircleMovement extends Application
{
//create the panes to handle the game
BorderPane mainPane = new BorderPane();
GridPane infoPane = new GridPane();
Pane gameField = new Pane();
Scene scene;
//create circle/movement properties
double increment = 5.0;
double radius = 10;
double x = radius, y = radius;
//create Rectangle properties
double Rwidth = 80;
double Rheight = 20;
//create player and objects
Circle player = new Circle(x, y, radius);
Rectangle r1 = new Rectangle(0, 100, Rwidth, Rheight);//Rectangle(int x, int y, int width, int height)
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage) throws Exception
{
setPanes();
gameField.getChildren().addAll(r1, player);
moveCircleOnKeyPress(scene, player);//pass to player movement method
//set the stage
stage.setScene(scene);
stage.show();
}
public void moveCircleOnKeyPress(Scene scene, Circle player)
{//player movement
scene.setOnKeyPressed((KeyEvent event) ->
{
switch (event.getCode())
{
case UP:
player.setCenterY(player.getCenterY() - increment);//move player
//if (player.getBoundsInLocal().intersects(r1.getBoundsInLocal()))
if (Shape.intersect(player, r1).getBoundsInLocal().getWidth() != -1)
{
player.setCenterY(player.getCenterY() + increment);
}
break;
case RIGHT:
player.setCenterX(player.getCenterX() + increment);
if (Shape.intersect(player, r1).getBoundsInLocal().getWidth() != -1)
{
player.setCenterX(player.getCenterX() - increment);
}
break;
case DOWN:
player.setCenterY(player.getCenterY() + increment);
if (Shape.intersect(player, r1).getBoundsInLocal().getWidth() != -1)
{
player.setCenterY(player.getCenterY() - increment);
}
break;
case LEFT:
player.setCenterX(player.getCenterX() - increment);
if (Shape.intersect(player, r1).getBoundsInLocal().getWidth() != -1)
{
player.setCenterX(player.getCenterX() + increment);
}
break;
}
});
}
public void setPanes()
{
infoPane.setMaxSize(200, 200);
mainPane.setMaxSize(800, 800);
mainPane.setCenter(gameField);//place the main game inside the center
mainPane.setRight(infoPane);
mainPane.setPadding(new Insets(10, 10, 10, 10));
mainPane.setStyle("-fx-border-color: red");
gameField.setStyle("-fx-background-color: white");
gameField.setStyle("-fx-border-color: black");
gameField.setMaxSize(600, 600);
scene = new Scene(mainPane, 800, 800);
player.setFill(Color.RED);
}
}

Highlighting rectangle when more than half overlaps

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 ;
}

Resources