I am trying to setup an event handler for a click, to added on a scene within a JFXPanel:
scene.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> System.out.println("Button pressed: " + event.getButton().name()));
Strangely enough, if I hold the primary button and click the secondary or middle button, the button returned by event.getButton()
is the primary button, not the button that triggered the click.
So for example if I hold the primary button down and click on the middle button, the code above will have the console print Button pressed: PRIMARY
.
I tried in a purely JavaFX application, and there getButton()
always returns the button pressed; the issue is thus with the JFXPanel.
Is there an explanation, or workaround for this strange behavior?
Here is a complete example:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) { SwingUtilities.invokeLater(Test::initAndShowGUI); }
private static void initAndShowGUI() {
// Setup the JFXPanel inside a JPanel
JFrame frame = new JFrame();
final JFXPanel fxPanel = new JFXPanel();
frame.add(fxPanel);
frame.setSize(300, 200);
frame.setVisible(true);
Platform.runLater(() -> initFX(fxPanel));
}
private static void initFX(JFXPanel fxPanel) {
Group root = new Group();
Scene scene = new Scene(root);
// The event handler that incorrectly returns the button held instead of the one pressed
scene.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> System.out.println("Button pressed: " + event.getButton().name()));
fxPanel.setScene(scene);
}
}
Run the code above and click the secondary button while holding down the primary to get the message "Button pressed: PRIMARY" instead of "Button pressed: SECONDARY".
I tested it out and the behavior is present in both Java 10 and Java 14.It is also present for MOUSE_RELEASED
and MOUSE_CLICKED
.
It's a current bug in Java.
The source of the bug is the function mouseButtonToEmbedMouseButton
in class SwingEvents
of package com.sun.javafx.embed.swing
.
The function is supposed to convert a Swing mouse button into a JavaFX mouse button.
As an illustration, the following code
SwingEvents.mouseButtonToEmbedMouseButton(intButton, MouseEvent.BUTTON1_DOWN_MASK)
will always return 1
, no matter what value intButton
takes.
Looking at the source code for mouseButtonToEmbedMouseButton
, the problem is the if statement
if ((extModifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0){
abstractButton = AbstractEvents.MOUSEEVENT_PRIMARY_BUTTON;}
where extModifiers
is the second argument of the function. So if extModifiers
is MouseEvent.BUTTON1_DOWN_MASK
, the abstractButton
(what is returned) will always be set to 1.