I have two overlapped component which aren't related (different nodes from different parents). The front component absorbs all the events which is my problem. The component's hierarechy cannot be changed, and I am looking to solve this programmatically.
I want the front component to be transparent to the MouseClick events (similar to the MouseTransparent(true) behavior but only for MouseClick) so the background component could capture the right click event.
Is there a way to make the front component half transparent to specific events?
Note : I wanna find a way to ignore the Left MouseClick on the front.
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
initScene(primaryStage);
}
public void initScene(Stage primaryStage) {
/* Initialization */
StackPane root = new StackPane();
Group frontContainer = new Group();
Group backContainer = new Group();
Scene _scene = new Scene(root, 500, 500);
Rectangle frontComponent = new Rectangle(180, 180);
Rectangle backComponent = new Rectangle(200, 200);
frontComponent.setFill(Color.GREY);
backComponent.setFill(Color.ORANGE);
/* Scene Graph construction */
frontContainer.getChildren().add(frontComponent);
backContainer.getChildren().add(backComponent);
root.getChildren().add(backContainer);
root.getChildren().add(frontContainer);
backContainer.toBack();
frontContainer.toFront();
// --- MouseEvents Handlers
// --------------------------------------------------------
frontContainer.setOnMouseClicked((mouseClicked) -> {
if (mouseClicked.getButton() == MouseButton.PRIMARY)
System.out.println("FrontComponent right click");
});
frontContainer.setOnDragDetected((mouseDrag) -> {
// Does something very important for a DragNDrop feature that
// depends on Right Mouse Click
});
backComponent.setOnMouseClicked((mouseClicked) -> {
if (mouseClicked.getButton() == MouseButton.SECONDARY)
System.out.println("BackComponent left click");
});
// ------------------------------------------------------------------------------------
// frontContainer.setOnMouseClicked(null); /* Disables the handling but
// still consumes the event */
primaryStage.setScene(_scene);
primaryStage.show();
}
}
After deep thinking I found that the only feasable solution without altering the whole hieararechy is altering the Event Dispatch. More information about Event processing can be found here.
I managed to alter the event processing by applying an EventFilter to the parent node of the containers. The EventFilter is called before an EventHandler therefore I captured the MouseClick event and processed it. when I calculated that the back component was in a the background, I consumed the leftClick on the frontComponent and then fired a new MouseClick Event towards the backComponent.
This new firedEvent has as a target my backComponent which ignores my front component, and that's exacly what I need.
// --- MouseEvents Handlers --------------------------------------------------------
frontContainer.setOnMouseClicked((mouseClicked) -> {
if (mouseClicked.getButton() == MouseButton.PRIMARY)
System.out.println("FrontComponent right click");
if (mouseClicked.getButton() == MouseButton.SECONDARY)
System.out.println("FrontComponent left click");
});
frontContainer.setOnDragDetected((mouseDrag) -> {
// Does something very important for a DragNDrop feature that
// depends on Right Mouse Click
});
backComponent.setOnMouseClicked((mouseClicked) -> {
if (mouseClicked.getButton() == MouseButton.SECONDARY)
System.out.println("BackComponent left click");
});
root.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
System.out.println("click FILTERED");
if (event.getButton() == MouseButton.SECONDARY && !dispatchFlag) {
if (event.getX() >= backContainer.getLayoutX()
&& backContainer.getLayoutX() + backComponent.getWidth() >= event.getX()) {
System.out.println("It is behind me !");
MouseEvent monEvent = new MouseEvent(event.getSource(), backContainer, MouseEvent.MOUSE_CLICKED,
event.getSceneX(), event.getY(), event.getScreenX(), event.getScreenY(),
event.getButton(), event.getClickCount(), false, false, false, false,
event.isPrimaryButtonDown(), event.isMiddleButtonDown(), event.isSecondaryButtonDown(),
event.isSynthesized(), event.isPopupTrigger(), event.isStillSincePress(),
event.getPickResult());
event.consume();
dispatchFlag = true;
Event.fireEvent(backComponent, monEvent);
System.out.println("Event dispatched !");
}
}
dispatchFlag = false;
}
});
// ------------------------------------------------------------------------------------