Javafx retrievable drag and drop bug - javafx

I am creating a javafx application with a drag and drop feature. I need to be able to drag from one pane and drop to the next pane. After dropping, all dropped nodes need to be accessible. The drag and drop is working smoothly. However, I am having a problem in which not all nodes are accessible after being dropped. I would drag a node and click on it and sometimes it would respond, but sometimes a random node would no longer exhibit capabilities when I clicked on it. There was no noticeable pattern (that I could see) about which nodes stopped working and which always worked. If anyone has any idea as to why this might be a problem or how to fix it, it would be greatly appreciated. I have looked everywhere and found only one similar issue a few years ago but the page with their solution had been removed.
I will briefly explain how I set up my code structure.
The nodes I am dragging extend StackPane and I drag these nodes onto a center Pane from an ImageView in VBox. However, both of these are children of a Main layout that extends BorderPane which I have called MainEntrance.
In my node's class I have onMousePressed and onMouseDragged features as well as onContextMenuRequested features. In the class of the MainEntrance, I have drag features, as well, that handle when an object is first dragged onto the child Pane from the other child VBox.
The following are the relevant classes/methods:
VBox class: (where nodes are dragged from)
ImageView iV= new ImageView(image);
iV.setId("image");
iV.setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragAndDrop(iV, iV.getId(), event);
}
});
I have drag three ImageViews and these ImageViews are children of a VBox.
MainEntrance:
In this class I have a method that registers my drag events to my pane as such:
PView is my class extension of Pane
public void registerOnDragDropped(PView pView){
pView.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
event.acceptTransferModes(TransferMode.ANY);
System.out.println("drag over registered");
}
});
pView.setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
final String id = db.getString();
double mousePosY = event.getY();
double mousePosX = event.getX();
deviceCounter += 1;
pView.createBoardView(id, deviceCounter, mousePosX, mousePosY);
event.setDropCompleted(true);
}
});
pView.setOnDragExited(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
event.acceptTransferModes(TransferMode.ANY);
//computerView.setEffect(null);
event.consume();
}
});
}
Note: this method is in my MainEntrance class (BorderPane). In this class is when I initialize all other child nodes of MainEntrance.
Node: (draggable nodes extend StackPane)
my node's handlers are in a method that I called makeDraggable()
public void makeDraggable(Node node) {
this.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
node.getScene().setCursor(Cursor.MOVE);
System.out.println("makeDraggable mouse pressed");
}
});
node.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
node.getScene().setCursor(Cursor.DEFAULT);
}
});
node.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
node.setTranslateX(node.getTranslateX() + event.getX() - 35);
node.setTranslateY(node.getTranslateY() + event.getY() - 35);
event.consume();
node.toFront();
}
});
}
If any other information is needed please let me know. Thank you in advance!

I have figured out the solution to this problem.
The problem was in the rendering order of an attachment I had placed on the node. So when I drag the node on to the screen, I have another node (a line placed on a Pane) that binds the center of the screen to the node. I did not initially think that this was the problem, so I did not even include that class or snippet. However, after playing around with it I realized that this indeed was the problem. I had to add the line to the top of the rendering order. This can be done in the following way in whatever method allows you to create a new node:
Pane line = new Pane();
//add whatever code binds your line to desired places
getChildren.add(0, line);
//add the other node as child

Related

JavaFX mouseEvent method getSceneX() stops after holding mouse buttons

public void setListeners() {
for(Scene s : Org.scenes) {
s.setOnMouseMoved(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
mouseX = event.getSceneX();
mouseY= event.getSceneY();
}
});
}
}
this code works well, however whenever mouse1 or mouse2 is held down, the code does not work, and the mouseX and mouseY variables stay at the same value, despite the mouse being moved around. I cannot understand why holding the mouse buttons pause the updating of the variables.
When you press any button on the mouse that's no more considered as mouse move event, instead it becomes a mouse click event and if you hold the mouse button and move the cursor it will be a mouse drag event. So try adding new listeners setOnMouseClicked() or setOnMouseDragged()
s.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//what you want to do
}
});
s.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//what you want to do
}
});

JavaFX secondary stage onCloseRequest

I have a little issue close a secondary stage after clicking the close at the top right corner.
I'm using fxml with controller class, i need a way to handle this situation.
Here is what i do but i get a nullpointer exception :
#Override
public void initialize(URL location, ResourceBundle resources) {
Stage stage = (Stage) tbTabPaneHome.getScene().getWindow();
stage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
}
Because the stage not yet intialized completly, so any other ideas ?
Since the Scene nor the Stage are created yet, you can't call them or you get a NPE, as you already mentioned.
One way to install the event handler on the stage will be listening to changes in the sceneProperty() of tbTabPaneHome.
Once the node is added to the scene, that property will give you the Scene instance.
But the scene is not added to the Stage yet, so you need to wait till this is done, with Platform.runLater():
public void initialize() {
tbTabPaneHome.sceneProperty().addListener((obs, oldScene, newScene) -> {
Platform.runLater(() -> {
Stage stage = (Stage) newScene.getWindow();
stage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
});
});
}
Did you try to deal with your secondary stage entirely in the main stage controller?
I want to hide or show a help windows from a button or help menu in my main application controller. Something like the following:
public Button helpBtn;
Stage anotherStage = new Stage();
boolean secondaryInitialyzed = false;
boolean secondaryShowing = false;
public void showOrHideHelp(ActionEvent actionEvent) throws IOException {
if (!secondaryInitialyzed){
Parent anotherRoot = FXMLLoader.load(getClass().getResource("mySecondaryStage.fxml"));
anotherStage.setTitle("Secondary stage");
Scene anotherScene = new Scene(anotherRoot, 500, 350);
anotherStage.setScene(anotherScene);
secondaryInitialyzed = true;
}
if (secondaryShowing){
anotherStage.hide();
secondaryShowing = false;
helpBtn.setText("Show Help");
}
else {
anotherStage.show();
secondaryShowing = true;
helpBtn.setText("Hide Help");
}
It does work, and there might be a way for you to handle your setOnCloseRequest within the main controller.
I have the opposing issue, i.e preventing closing the secondary stage window by clicking the close at the top right corner. I'll look into setOnCloseRequest and see if there is a way there.
I also have an other unrelated problem: can I position the secondary in reference to the primary one?

JavaFX opposite function of .requestFocus()?

I'm looking for function in JavaFX, where I can "disable or dismiss" the requested focus.
Here is a screenshot of my program: Screenshot
Every cell is filled with an Eventhandler (onMouseEntered and onMouseExited) and in every onMouseEntered function I have to request the focus like this:
label.requestFocus(). I need to do that, because I'm using KeyEvents to change the content of the cell.
It works fine, but there is a bug: When I move out of the scrollPane, there is still the requested focus on the last entered cell.
How can I solve that issue, without requesting focus for everything around the Scrollpane to fix this bug? Is there a function, where I can dismiss the requested focus, so after exiting a cell, it'll dismiss the focus.
Best regards,
My code:
arbeitetLabel.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
arbeitetLabel.requestFocus();
arbeitetLabel.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent keyEvent) {
// Do some keyEvent Stuff
}
});
}
});
arbeitetLabel.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
// Dismiss the requested Focus here?
}
});
I'm passing the focus to the parent, that's it!
arbeitetLabel.getParent().requestFocus()

How disable handling mouse events on content area in jfxtras window?

I can drag jfxtras Window by clicking on ImageView in window area. I need to handle mouse move, swipe... events by ImageView. How disable handling mouse events on content window area?
Note: You can drag window by ImageView in content area, but can't by Button.
I found solution. But it seems like hack:
imageView.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
window.setMovable(false)
}
});
imageView.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
window.setMovable(true);
}
});

JavaFX MouseEvent not firing after moving nodes in GridPane

I am attempting to create a "draggable" histogram UI with JavaFX. I have a ScrollPane containing a GridPane with 1 column and lots of rows. In each row is an HBox containing a label. Every 10 rows, there is also an HBox containing a Line.
I tried to make the HBoxes containing lines draggable by setting onMousePressed, onMouseDragged, and onMouseReleased event handlers (shown below). It works if I drag and release an hbox-line above its starting point - it ends up in whatever grid row I put it in, and I can click and drag it again. However, if I drag and release a line below its starting point, I can't get any more mouseEvents for that hBox. I tried adding log statements everywhere, nothing. I tried setting onMouseOver, it also was not fired.
Why would moving an hbox around the grid like this work for dragging up but not down?
lineContainer.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
EventTarget target = mouseEvent.getTarget();
lastY = mouseEvent.getSceneY();
}
});
lineContainer.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
Node target = (Node) mouseEvent.getTarget();
HBox hBox = null;
if (target instanceof HBox) {
hBox = (HBox) target;
}
else if (target instanceof Line) {
hBox = (HBox) target.getParent();
}
else { //should never happen
log.info("target not hbox or line: " + target.getClass());
}
if (mouseEvent.getSceneY() <= (lastY - 15)) {
int row = GridPane.getRowIndex(hBox);
GridPane.setRowIndex(hBox, --row);
lastY = mouseEvent.getSceneY();
lastRow = row - 1;
} else if (mouseEvent.getSceneY() >= (lastY + 15)) {
int row = GridPane.getRowIndex(hBox);
GridPane.setRowIndex(hBox, ++row);
lastRow = row - 1;
lastY = mouseEvent.getSceneY();
}
}
});
lineContainer.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
Node tar = (Node) mouseEvent.getTarget();
HBox hBox = null;
if (tar instanceof HBox) {
hBox = (HBox) tar;
}
else if (tar instanceof Line && tar.getParent() instanceof HBox) {
hBox = (HBox) tar.getParent();
}
else { //should never happen
log.info(mouseEvent.getTarget().getClass().toString());
}
}
});
UPDATE: I managed to get it working by creating a new HBox, resetting the onMouse... handlers, and copying its children every time the mouse is released. But I still don't know what was causing the original issue...
The following isn't a direct solution for you, but I wanted to say I have a similar problem and share my observations.
My application allows dragging in both axes (X, Y). All I've been able to figure out that some invisible element is 'obscuring' the MouseEvent hitbox. Testing it using an 'MS minesweeper' approach, shows this interfering area to extend from coords (0,0) of the root to (maxX,maxY) of another Node I have in the scene which is a layer above.
My problem was solved by changing z-order of Parent objects (let's call them layers) containing the Nodes that didn't receive their MouseEvents.
JavaFX MouseEvent doc explains that it is only the top level node which receives the event:
https://docs.oracle.com/javafx/2/api/javafx/scene/input/MouseEvent.html
Also look at the pickOnBounds property: JavaFX: How to make a Node partially mouse transparent?

Resources