JavaFx: check if the mouse is on node's children - java

I would like to know if there is a way to determine if the mouse collides with a node's children, in less words, In the example below, If I click on the Group the output is:
"Group!"
If I click on the image the output is:
"Group!
Image!"
Is there a way to put code in the "group.setOnMousePressed" in order to check if the mouse in on the image and in that case don't do anything and just execute what is in the "group.setOnMousePressed", in order to have this output clicking on image:
"Image!"
Please find below a SSCCE:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class SSCCEForSO extends Application {
#Override
public void start(Stage primaryStage) {
AnchorPane anchor= new AnchorPane();
Group group= new Group();
ImageView image= new ImageView();
image.setImage(ImageUtil.getImage("wave.png"));
ImageView image2= new ImageView();
image2.setImage(ImageUtil.getImage("pause15.png"));
HBox hBox = new HBox();
hBox.setPrefSize(200, 200);
hBox.setAlignment(Pos.CENTER);
hBox.setStyle("-fx-padding: 10;-fx-background-color: firebrick;-fx-background-radius: 5;");
hBox.getChildren().add( image);
hBox.getChildren().add( image2);
group.getChildren().add(hBox);
group.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("Group!");
}
});
image2.onMouseClickedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("Image!");
}
});
anchor.getChildren().add(group);
Scene scene = new Scene(anchor, 800, 600);
primaryStage.setScene( scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Thank you in advance

Suggested Solution: Consume the Event
Consume the mouse event when you handle it in your on clicked handler for the image:
image2.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
mouseEvent.consume();
System.out.println("Image! " + mouseEvent.getTarget());
}
});
This will prevent the event from continuing to bubble up the event dispatch chain. Read the section of the Oracle JavaFX documentation on handling events if you need to understand what this actually means.
Alternate Solution: Check the Event Target
Note, I also added mouseEvent.getTarget() to the handler. You can use the result of this call to evaluate the target of the event and take action based upon that. For example, the following code would also work:
hBox.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getTarget() == hBox) {
System.out.println("hBox! " + mouseEvent.getTarget());
} else {
System.out.println("hBox Ignored! " + mouseEvent.getTarget());
}
}
});
image2.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("Image! " + mouseEvent.getTarget());
}
});
Notes for the above code:
I changed the handler settings to all consistently use onMouseClicked rather than one onMousePressed handler and one onMouseClicked handler. This is important because mouse clicks and mouse presses are distinct events.
I used set the onClickHandler on the hBox rather than the enclosing group because the hBox is actually the target of the event rather than the enclosing group. The hBox covers the group completely, so the user cannot directly click on the group as an event target, they can only click on the covering hBox.
Executable Sample
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class SSCCEForSO extends Application {
#Override
public void start(Stage primaryStage) {
AnchorPane anchor= new AnchorPane();
Group group= new Group();
ImageView image= new ImageView();
image.setImage(new Image("http://icons.iconarchive.com/icons/custom-icon-design/pretty-social-media-2/64/Google-wave-icon.png"));
ImageView image2= new ImageView();
image2.setImage(new Image("http://icons.iconarchive.com/icons/custom-icon-design/pretty-office-8/64/Pause-icon.png"));
HBox hBox = new HBox();
hBox.setPrefSize(200, 200);
hBox.setAlignment(Pos.CENTER);
hBox.setStyle("-fx-padding: 10;-fx-background-color: firebrick;-fx-background-radius: 5;");
hBox.getChildren().add( image);
hBox.getChildren().add( image2);
group.getChildren().add(hBox);
group.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("Group!" + mouseEvent.getSource());
}
});
image2.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("Image!" + mouseEvent.getSource());
mouseEvent.consume();
}
});
anchor.getChildren().add(group);
Scene scene = new Scene(anchor, 800, 600);
primaryStage.setScene( scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Related

Is it possible to change the white file icon that appears when dragging a node with setOnDragDetected? (JavaFX) [duplicate]

This question already has answers here:
Using JavaFX with drag and drop, is it possible to have a ghost of the dragged object follow the cursor?
(2 answers)
JavaFX drag & drop with custom node beside mouse icon
(1 answer)
Closed 11 months ago.
GOAL:
I wish to change the white file icon that appears when dragging a node that has implemented setOnDragDetected. More specifically, I'm recreating chess in JavaFX, and want to have a chess piece image appear when dragging, instead of the current white file image, like as shown below.
CODE: I've created a minimal example:
package application;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
public class StackOverflowSample extends Application {
#Override
public void start(Stage primaryStage) {
try {
primaryStage.setTitle("Chess Sample");
Group root = new Group();
Scene scene = new Scene(root, 400, 200);
/* 1. Setting up Chess squares (aka stackpanes)*/
StackPane source1 = new StackPane(new Rectangle(100,100,Color.BLUE));
StackPane target1 = new StackPane(new Rectangle(100,100,Color.LIGHTBLUE));
source1.setLayoutX(50);
source1.setLayoutY(50);
target1.setLayoutX(250);
target1.setLayoutY(50);
/*2. Setting up the chess piece to move*/
Image whiteKnightImg = new Image("https://www.kindpng.com/picc/m/22-223299_white-knight-chess-png-transparent-png.png", 90,90,true,true);
ImageView whiteKnightView = new ImageView(whiteKnightImg);
source1.getChildren().add(whiteKnightView);
/* 3. Adding Drag and Drop Functionality*/
source1.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
Dragboard db = source1.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.putString("hasPiece");
db.setContent(content);
event.consume();
}
});
target1.setOnDragOver(new EventHandler <DragEvent>() {
#Override
public void handle(DragEvent event) {
if (event.getGestureSource() != target1 && event.getDragboard().hasString()) {
event.acceptTransferModes(TransferMode.MOVE);
}
event.consume();
}
});
target1.setOnDragDropped(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
target1.getChildren().add(whiteKnightView);
success = true;
}
event.setDropCompleted(success);
event.consume();
}
});
source1.setOnDragDone(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
if (event.getTransferMode() == TransferMode.MOVE) {
System.out.println("success");
}
event.consume();
}
});
root.getChildren().addAll(source1,target1);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
POSSIBLE SOLUTIONS:
Java Source Code. Maybe there's a way to inherit the Node class that implement the dragging related methods. I took a look at the source code for JavaFX.scene.Node and found the section related to setOnDragDetected, but didn't look into it any further.
public final void setOnDragDetected(
EventHandler<? super MouseEvent> value) {
onDragDetectedProperty().set(value);
}
public final EventHandler<? super MouseEvent> getOnDragDetected() {
return (eventHandlerProperties == null)
? null : eventHandlerProperties.getOnDragDetected();
}
/**
* Defines a function to be called when drag gesture has been
* detected. This is the right place to start drag and drop operation.
* #return the event handler that is called when drag gesture has been
* detected
*/
public final ObjectProperty<EventHandler<? super MouseEvent>>
onDragDetectedProperty() {
return getEventHandlerProperties().onDragDetectedProperty();
}
It's not possible. I've considered that it's not something that can be changed. believe it's possible that it's impossible to change this white icon, so I might just change my approach entirely. Instead of using StackPanes as squares that also hold images, I'm considering this tutorial.

JavaFX Dynamic Code

I want to immediately after increasing the value of i appear in the label
Example:
-in i=0 show 0
-in i=1 show 01
-in i=2 show 012
Can You Help me
import javafx.application.Application;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Example extends Application{
#Override
public void start (Stage primaryStage) {
Pane pane=new Pane();
Label label=new Label();
Button bt=new Button("Start");
pane.getChildren().addAll(bt,label);
bt.setOnAction(e->{
for (int i=0;i<10000000;i++) label.setText(label.getText()+i);
});
Scene scene = new Scene(pane,1000,500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The problem is that you update the label's value while you are on the user interface's thread. JavaFX works with a model where the updates are done at each 'tick' (60 fps). All the updates done are only visible once your eventhander's code has finished.
Additionally, given that this is a long running task it will result in an unresponsive user interface.
You should use a Worker to do the long running task. See the tutorial on asynchronous processing. Note that it will not guarantee that you will see all values as the worker can be quicker than the user interface updates and the system will coalesce these updates.
You can use Timeline to accomplish this task.
import java.util.concurrent.atomic.AtomicLong;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author blj0011
*/
public class JavaFXApplication177 extends Application
{
#Override
public void start(Stage primaryStage)
{
AtomicLong i = new AtomicLong();
Label label = new Label();
Button btn = new Button();
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(.5), (ActionEvent event) -> {//This controls how fast this should run. This example happens every half of a second
label.setText(label.getText() + Long.toString(i.getAndIncrement()));
}));
timeline.setCycleCount(10000000);//This controls the amount of time this should run
timeline.setOnFinished(event -> {//This tells what to do once cycle count is reached
btn.setDisable(false);
});
btn.setText("Start");
btn.setOnAction((ActionEvent event) -> {
btn.setDisable(true);
timeline.play();
});
StackPane stackPane = new StackPane(label);
VBox root = new VBox(stackPane, new StackPane(btn));
VBox.setVgrow(stackPane, Priority.ALWAYS);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Test5 extends Application {
private String text = "";
private int i;
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
StackPane pane = new StackPane();
Label lblText = new Label("");
pane.getChildren().add(lblText);
new Thread(new Runnable() {
#Override
public void run() {
try {
for (i=0;i<10000;i++) {
Platform.runLater(new Runnable() { // Run from JavaFX GUI
#Override
public void run() {
lblText.setText(lblText.getText()+i);
}
});
Thread.sleep(200);
}
}
catch (InterruptedException ex) {
}
}
}).start();
Scene scene = new Scene(pane, 200, 50);
primaryStage.setTitle("FlashText"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
}
Service<Void> service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
//Your First Task #1
//Here UI won't be interrupted
Platform.runLater(new Runnable() {
#Override
public void run() {
//Your Second Task After Completion Of First One #2
}
});
return null;
}
};
}
};
service.start();
}
#1. The task that you want to perform in the background ex. loading the data has to be placed here. It's working great for me.
#2. Once the background task is finished this thread will be executed so Ui and background thread will run separately and smoothly.
I know it's too late for this answer but I just wanted to share what I did this might help!

setOnMouseDragged not working on browser view

I'm writing a simple application in java using JxBrowser engine but i'm stuck at the very beginning. In my code, there is an undecorated stage that i want to make it draggable. To do so, searched and found the following link:
How to drag undecorated window
So I set mousePressed and MouseDragged event on stackPane but only mousePressed event gets fired and mouseDragged event no way gets fired. Any idea of what's the problem?
Thanks in advance.
import com.teamdev.jxbrowser.chromium.Browser;
import com.teamdev.jxbrowser.chromium.javafx.BrowserView;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
private static double xOffset = 0;
private static double yOffset = 0;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Platform.setImplicitExit(false);
Browser browser = new Browser();
BrowserView browserView = new BrowserView(browser);
StackPane pane = new StackPane();
pane.getChildren().add(browserView);
Scene scene = new Scene(pane, 380, 500);
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(scene);
pane.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("mouse pressed");
xOffset = primaryStage.getX() - event.getScreenX();
yOffset = primaryStage.getY() - event.getScreenY();
}
});
pane.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("mouse dragged");
primaryStage.setX(event.getScreenX() + xOffset);
primaryStage.setY(event.getScreenY() + yOffset);
}
});
primaryStage.show();
}
}
Since jxbrowser requires license I couldn't test it... So I replaced that with Label and it works fine.. So my guess is that you are trying to drag by clicking on the browser itself and not the StackPane.. Try clicking at the corner of the Stage or else add stack pane to VBox and setPadding to it.. And try clicking at the corner.. If your clicking on the browser then browser's mouse events will be trigerred..
Proof:
The correct code
package RezRem;
import com.teamdev.jxbrowser.chromium.Browser;
import com.teamdev.jxbrowser.chromium.javafx.BrowserView;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
private static double xOffset = 0;
private static double yOffset = 0;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Platform.setImplicitExit(false);
Browser browser = new Browser();
BrowserView browserView = new BrowserView(browser);
StackPane pane = new StackPane();
pane.getChildren().add(browserView);
pane.setPadding(new Insets(10,10,10,10));
Scene scene = new Scene(pane, 380, 500);
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(scene);
pane.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("mouse pressed");
xOffset = primaryStage.getX() - event.getScreenX();
yOffset = primaryStage.getY() - event.getScreenY();
}
});
pane.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("mouse dragged");
primaryStage.setX(event.getScreenX() + xOffset);
primaryStage.setY(event.getScreenY() + yOffset);
}
});
browser.loadURL("http://www.google.com");
primaryStage.show();
}
}
Reason
The jxbrowser extends till the edge so the stack pane wasn't on top neither was it visible on the sides so the mouse listener's never got triggered by setting the padding of stack pane there was 10px gap on all four sides where, if clicked, triggered the mouse events and thereby solves the problem..
I ran into this same problem. I ended up solving it by using the setMouseEventsHandler method on BrowserView which does seem to receive all the events you need (although it does not seem to receive MouseEvent.MOUSE_DRAGGED events unless they have been rebranded as MouseEvent.MOUSE_MOVED events).
Rectangle[] dragIncludeRects = ...; // The area which is draggable (ie the title bar)
Rectangle[] dragExcludeRects = ...; // Exclusions (ie a close button on the title bar)
BrowserView popupView = ...;
JDialog popupFrame = ...; // could also be JFrame
Point dragOffset = null;
popupView.setMouseEventsHandler(new InputEventsHandler<MouseEvent>() {
public boolean handle(MouseEvent event) {
switch(event.getID()) {
case MouseEvent.MOUSE_PRESSED:
if (
dragIncludeRects.exists((rect) => rect.contains(event.getPoint())) &&
!dragExcludeRects.exists((rect) => rect.contains(event.getPoint()))
) {
dragOffset = SwingUtilities.convertPoint(
popupView, event.getPoint(), popupFrame);
} else {
dragOffset = null;
}
break;
case MouseEvent.MOUSE_RELEASED:
dragOffset = null;
break;
case MouseEvent.MOUSE_MOVED:
if (dragOffset != null) {
// Note I tried using the position from the event but it doesn't work well
val position = MouseInfo.getPointerInfo().getLocation();
popupFrame.setLocation(position.x - offset.x, position.y - offset.y);
}
}
return false;
}
})

Drag and dropping with visual indication

I am trying to implement a drag and drop between 2 objects.
The problem is when I drag from one rectangle to the other I need the start of a line to be connected on the source rectangle and the end of the line to follow the mouse around.
Then when I drop at the second rectangle the end of the line should bind at it and a string passed.
On one hand using onmouseclicked, dragged, and released I managed to make the line follow the mouse around but am not able to make the second rectangle understand that the mouse was released on it (as shown in the code below)
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Test extends Application {
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Pane root = new BorderPane();
Rectangle rect1=new Rectangle(100,50);
rect1.setFill(Color.AQUAMARINE);rect1.setStroke(Color.BLACK);
Label rect1_label=new Label("Rectangle 1");
rect1_label.setLayoutX(20);rect1_label.setLayoutY(15);
rect1.setLayoutX(220);rect1.setLayoutY(240);
Line line=new Line (rect1.getLayoutX()+rect1.getWidth(),rect1.getLayoutY()+rect1.getHeight()/2,
rect1.getLayoutX()+rect1.getWidth(),rect1.getLayoutY()+rect1.getHeight()/2);
line.startXProperty().bind(rect1.translateXProperty().add(rect1.getLayoutX()+rect1.getWidth()));
line.startYProperty().bind(rect1.translateYProperty().add(rect1.getLayoutY()+rect1.getHeight()/2));
line.getStrokeDashArray().setAll(10.0, 5.0);
Rectangle rect2=new Rectangle(100,50);
rect2.setFill(Color.BISQUE);rect2.setStroke(Color.BLACK);
Label rect2_label=new Label("Rectangle 2");
rect2_label.setLayoutX(20);rect2_label.setLayoutY(15);
rect2.setLayoutX(600);rect2.setLayoutY(240);
root.getChildren().addAll(rect1,rect2,line);
rect1.setOnMousePressed(new EventHandler<MouseEvent>(){
public void handle(MouseEvent event){
line.setVisible(true);
line.toBack();
line.setEndX(event.getX());
line.setEndY(event.getY());
orgSceneX = event.getSceneX();
orgSceneY = event.getSceneY();
orgTranslateX = ((Rectangle)(event.getSource())).getTranslateX();
orgTranslateY = ((Rectangle)(event.getSource())).getTranslateY();
line.setEndX(event.getSceneX());
line.setEndY(event.getSceneY());
}
});
rect1.setOnMouseDragged(new EventHandler<MouseEvent>(){
public void handle(MouseEvent event)
{
double offsetX = event.getSceneX();
double offsetY = event.getSceneY();
double newTranslateX = offsetX;
double newTranslateY = offsetY;
line.setEndX(newTranslateX);
line.setEndY(newTranslateY);
}
});
rect1.setOnMouseReleased(new EventHandler<MouseEvent>(){
public void handle(MouseEvent event)
{
line.setVisible(false);
}
});
Scene scene = new Scene(root);
primaryStage.setTitle("Nodes test 1");
primaryStage.setMinWidth(1000);
primaryStage.setMinHeight(600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
.
I also know how to drag and drop a value or string from one object to another like that
.
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Test2 extends Application {
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Pane root = new BorderPane();
Rectangle rect1=new Rectangle(100,50);
rect1.setFill(Color.AQUAMARINE);rect1.setStroke(Color.BLACK);
Label rect1_label=new Label("Rectangle 1");
rect1_label.setLayoutX(20);rect1_label.setLayoutY(15);
rect1.setLayoutX(220);rect1.setLayoutY(240);
Line line=new Line (rect1.getLayoutX()+rect1.getWidth(),rect1.getLayoutY()+rect1.getHeight()/2,
rect1.getLayoutX()+rect1.getWidth(),rect1.getLayoutY()+rect1.getHeight()/2);
line.startXProperty().bind(rect1.translateXProperty().add(rect1.getLayoutX()+rect1.getWidth()));
line.startYProperty().bind(rect1.translateYProperty().add(rect1.getLayoutY()+rect1.getHeight()/2));
line.getStrokeDashArray().setAll(10.0, 5.0);
Rectangle rect2=new Rectangle(100,50);
rect2.setFill(Color.BISQUE);rect2.setStroke(Color.BLACK);
Label rect2_label=new Label("Rectangle 2");
rect2_label.setLayoutX(20);rect2_label.setLayoutY(15);
rect2.setLayoutX(600);rect2.setLayoutY(240);
root.getChildren().addAll(rect1,rect2,line);
rect1.setOnDragDetected(new EventHandler<MouseEvent>(){
#Override public void handle(MouseEvent event){
Dragboard db = rect1.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.putString("rec");
db.setContent(content);
event.consume();
}
});
rect2.setOnDragOver(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
event.consume();
}
});
rect2.setOnDragDropped(new EventHandler<DragEvent>(){
#Override public void handle(DragEvent event){
final Dragboard db = event.getDragboard();
if (db.getString().equals("rec")){
System.out.println("Accepted");
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
}
});
Scene scene = new Scene(root);
primaryStage.setTitle("Nodes test 1");
primaryStage.setMinWidth(1000);
primaryStage.setMinHeight(600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
If I use them both something goes wrong
What can I do in order for the line to be drawn and the value to be passed at the same time?
Activate a "full press-drag-release gesture" by calling startFullDrag on the node where the drag originated. The differences between the types of drag gesture are detailed in the Javadocs for MouseEvent, but basically this allows mouse events to be delivered to nodes other than the node that originated the drag while the drag is in process.
Note, though, that you don't get a mouseReleased event on a node unless the mouse was pressed on that node (as far as I can tell). So you need to do a little bit of work to figure out if the release occurred over the second rectangle. I did this by setting a flag if the mouse entered the node during the drag and set it to false if it exits. You could also probably do this by looking for a mouse release on the underlying container, and seeing if the bounds of the node included the coordinates of the mouse event.
Also note in this example that I needed to call line.setMouseTransparent(true); to make sure the line didn't consume the mouse events.
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class DragAndDropWithLine extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Rectangle rect1 = new Rectangle(50, 50, 50, 100);
rect1.setFill(Color.YELLOW);
Rectangle rect2 = new Rectangle(200, 50, 50, 100);
rect2.setFill(Color.BLUE);
Line line = new Line();
line.setMouseTransparent(true);
pane.getChildren().addAll(rect1, rect2);
BooleanProperty dragging = new SimpleBooleanProperty();
BooleanProperty draggingOverRect2 = new SimpleBooleanProperty();
rect1.setOnDragDetected(event -> {
rect1.startFullDrag();
Point2D mouseSceneCoords = new Point2D(event.getSceneX(), event.getSceneY());
Point2D mousePaneCoords = pane.sceneToLocal(mouseSceneCoords);
line.setStartX(mousePaneCoords.getX());
line.setStartY(mousePaneCoords.getY());
line.setEndX(mousePaneCoords.getX());
line.setEndY(mousePaneCoords.getY());
pane.getChildren().add(line);
dragging.set(true);
});
pane.setOnMouseDragged(event -> {
if (dragging.get()) {
line.setEndX(event.getX());
line.setEndY(event.getY());
}
});
rect1.setOnMouseReleased(event -> {
if (draggingOverRect2.get()) {
pane.getChildren().remove(rect1);
rect2.setFill(Color.GREEN);
}
dragging.set(false);
draggingOverRect2.set(false);
pane.getChildren().remove(line);
});
rect2.setOnMouseDragEntered(event -> {
if (dragging.get()) {
draggingOverRect2.set(true);
}
});
rect2.setOnMouseDragExited(event -> draggingOverRect2.set(false));
primaryStage.setScene(new Scene(pane, 300, 200));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

How to make the MenuItems in a JavaFX context menu support an onMouseOver event

I'm looking for some guidance on how to proceed with a problem I'm having. I hava a JavaFX scene and within it some nodes (shapes) that connect to each other with one or more lines. I can right-click on a shape to bring up a context menu. Let's say this particular shape that was just right-clicked has 3 lines coming out of it (call them line1, line2, line3) and you want to use the context menu to delete one. You can select "line2" for example, and it will fire the onAction event to remove that line. That all works fine.
The trouble is, you don't know which of the 3 lines on the screen is line1 or line2 or line3 (unless of course they are labeled) and so you don't know which one you are about to remove until you remove it. What I would really like to do, for example, is to place my mouse over "line2" in the context menu and have line2 in the scene change color or something to indicate that it is the one about to be deleted (before I click the mouse). However, the only event I see supported by MenuItem is the onAction event for when it is clicked. Is there some way to give it onMouseOver functionality? if not, how could this feature be implemented?
Thanks!
Try this SSCCE:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ContextMenuDemo extends Application {
private DropShadow ds = new DropShadow();
#Override
public void start(Stage primaryStage) {
final Line line1 = new Line(60, 10, 150, 10);
final Line line2 = new Line(60, 30, 150, 50);
final Line line3 = new Line(60, 60, 150, 90);
final ContextMenu cm = new ContextMenu();
cm.getItems().add(getMenuItemForLine("line 1", line1));
cm.getItems().add(getMenuItemForLine("line 2", line2));
cm.getItems().add(getMenuItemForLine("line 3", line3));
final Rectangle rectangle = new Rectangle(70, 70, Color.TAN);
rectangle.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
if (e.getButton() == MouseButton.SECONDARY) {
cm.show(rectangle, e.getScreenX(), e.getScreenY());
}
}
});
Group root = new Group();
root.getChildren().addAll(rectangle, line1, line2, line3);
Scene scene = new Scene(root, 300, 250);
// load style of modified paddings for menuitems
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
private MenuItem getMenuItemForLine(String menuName, final Line line) {
Label menuLabel = new Label(menuName);
// apply style to occupy larger space for label
menuLabel.setStyle("-fx-padding: 5 10 5 10");
MenuItem mi = new MenuItem();
mi.setGraphic(menuLabel);
line.setStroke(Color.BLUE);
menuLabel.addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
line.setStroke(Color.RED);
line.setEffect(ds);
}
});
menuLabel.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
line.setStroke(Color.BLUE);
line.setEffect(null);
}
});
return mi;
}
public static void main(String[] args) {
launch(args);
}
}
with style.css
.menu-item {
/* -fx-skin: "com.sun.javafx.scene.control.skin.MenuItemSkin";*/
-fx-background-color: transparent;
-fx-padding: 0em; /* do not pad for item. we want to ccupy all spaces for graphics only */
}
.menu-item:focused {
-fx-background: -fx-accent;
-fx-background-color: -fx-selection-bar;
-fx-text-fill: -fx-selection-bar-text;
}
.menu-item .graphic-container {
-fx-padding: 0em; /* do not pad for graphics, label graphic pads itself */
}
.menu-item .label {
-fx-padding: 0em; /* do not pad for label, since there is no label text set */
-fx-text-fill: -fx-text-base-color;
}
Screenshot:
Description:
This is somewhat a bug that MenuItem does not work for MenuItem.addEventHandler(MouseEvent.MOUSE_ENTERED, ...) I think. As a workaround, we define new Label, register event handlers to it and set it as a graphic of menu item while the text(label) of menuitem intentionally left an empty. But the graphic of menu item does not (by default) occupy all space of menu item, so mouse events are not handled properly at the edges of menu item. To overcome this problem we reset all paddings of menuitem, menuitem's label and graphic through css. You can observe this by commenting out the style loading in the above code.
Here is a sample App I just created on an aproach to identify the lines:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Tooltip;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class MainTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
// TODO Auto-generated method stub
AnchorPane anchorPane = new AnchorPane();
Scene scene = new Scene(anchorPane);
stage.setScene(scene);
Line linea = new Line(0, 0, 50, 50);
linea.setFill(Color.BLACK);
final Tooltip t = new Tooltip("Line 1");
linea.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Line line = (Line) event.getSource();
line.setStroke(Color.RED);
t.show((Line) event.getSource(), event.getScreenX(),
event.getScreenY());
}
});
linea.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Line line = (Line) event.getSource();
line.setStroke(Color.BLACK);
t.hide();
}
});
anchorPane.getChildren().add(linea);
stage.show();
}
}
Hope it helps!

Categories

Resources