Reputation: 25
I'm making a JavaFX application and I want to do the following.
I present a couple buttons, when they are pressed, the GUI will minimize and waits for a mouseClick from the JNativeHook library. When a mouse is clicked it's x,y coordinats needs to be relayed back to the javaFx controller, the GUI maximizes and represents the data in the gui and gives me the possibility to do also other stuff with it.
I've got this behaviour working for one button but it is not good at all. My current implementation won't work if I want more buttons as you will see below and it would need alot of duplicate code, where I need to make a specific mouseListener and method in the layoutController for each button (aside from the buttonHandlers). I obviously do not want this, basicly I'm searching for a little framework to let the next NativeMouseEvent relay data in my buttonHandle method in the controller
This is what I got, the JavaFX layoutController with the button, has the following handle
@FXML
private void handleGetPos() {
try {
mouseManager.startMousePosChecker(cntrl);
mainGUI.minimizeGUI();
} catch (Exception e) {
e.printStackTrace();
}
}
The layoutController passes itself as cntrl
to my mouseManager, the manager just (un)registers a nativeHook to GlobalScreen
and adds a new mouseListener to it and passes the layoutController cntrl
with it. (passing the controller to it is also stupid if I want to use it another layoutControllers, since they are not the same type)
GlobalMouseListener mouseListener = new GlobalMouseListener();
mouseListener.setLayoutController(cntrl);
GlobalScreen.getInstance().addNativeMouseListener(mouseListener);
In the mouseListener this happends:
public void nativeMouseClicked(NativeMouseEvent e) {
Platform.runLater(() -> {
cntrl.setMousePos(ConvTypes.toStr(e.getX()), ConvTypes.toStr(e.getY()), e.getX(), e.getY() );
});
}
This is ugly, when the events happens it passes the x,y coordinates as strings to a specific method in my layoutController:
public void setMousePos(String sX, String sY, int x, int y) {
xPos.setText(sX);
yPos.setText(sY);
mainGUI.maximizeGUI();
mouseManager.stopMousePosChecker();
//other stuff
Obviously this implementation is not going to work for multiple buttons. Therefore I would like to build a little framework with mouseManager. Which registers/unregisters the nativeHook and makes only one mouseListener, which returns data to the mouseManager. This mouseManager then gives the necessary data to anyone who needs it. If I'm correct this sounds like a custom Event class? (Tried to make one but I have no idea how to implement it my layoutController or how to wrap it around the globalMouseEvents)
Anyway I think it would become something like this or maybe something with a lambda? (Note I actually have no idea):
@FXML
private void handleButton() {
xPos.setText(mouseManager<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
//necessery stuff goes here
So yes I have no idea how to get started on how to get that data to return in any buttonHandle() method or any method actually. I know my current code is bad and I want to make it better and reuse code instead of duplicate code, but this problem is currently breaking my head...
It is a long post, thanks for reading and helping!
Upvotes: 1
Views: 147
Reputation: 209705
Not sure I completely understand, but can you do something like this:
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseListener;
import org.jnativehook.GlobalScreen;
import javafx.scene.input.MouseEvent ;
import javafx.application.Platform ;
import javafx.event.EventType ;
import javafx.geometry.Point2D ;
import javafx.beans.property.ReadOnlyObjectWrapper ;
import javafx.beans.property.ReadOnlyObjectProperty ;
public class GlobalMouseListener implements NativeMouseListener {
private final EventType<MouseEvent> eventType ;
private final ReadOnlyObjectWrapper<Point2D> mouseLocation = new ReadOnlyObjectWrapper<>();
/**
* @param eventType The type of event to listen for. Should be one of
* MouseEvent.MOUSE_CLICKED, MouseEvent.MOUSE_PRESSED, or MouseEvent.MOUSE_RELEASED.
* The mouseLocationProperty() will be updated if a global mouse event
* matching the semantic type of the provided eventType occurs.
*/
public GlobalMouseListener(EventType<MouseEvent> eventType) {
this.eventType = eventType ;
GlobalScreen.addNativeMouseListener(this);
}
public ReadOnlyObjectProperty<Point2D> mouseLocationProperty() {
return mouseLocation.getReadOnlyProperty() ;
}
public final Point2D getMouseLocation() {
return mouseLocationProperty().get();
}
@Override
public void nativeMouseClicked(NativeMouseEvent e) {
if (eventType == MouseEvent.MOUSE_CLICKED) {
Platform.runLater(() -> mouseLocation.set(new Point2D(e.getX(), e.getY())));
}
}
@Override
public void nativeMousePressed(NativeMouseEvent e) {
if (eventType == MouseEvent.MOUSE_PRESSED) {
Platform.runLater(() -> mouseLocation.set(new Point2D(e.getX(), e.getY())));
}
}
@Override
public void nativeMouseReleased(NativeMouseEvent e) {
if (eventType == MouseEvent.MOUSE_RELEASED) {
Platform.runLater(() -> mouseLocation.set(new Point2D(e.getX(), e.getY())));
}
}
public void cleanup() {
GlobalScreen.removeNativeMouseListener(this);
}
}
Then your button handler just needs
@FXML
public void handleGetPos() {
GlobalMouseListener gml = new GlobalMouseListener(MouseEvent.MOUSE_CLICKED);
gml.mouseLocationProperty().addListener((obs, oldLocation, newLocation) -> {
double x = newLocation.getX();
double y = newLocation.getY();
gml.cleanup();
// restore window and process x, y...
});
mainGUI.minimizeGUI();
}
If you really want to minimize the boilerplate code, you can define
private void processMouseLocation(BiConsumer<Double, Double> pointProcessor) {
GlobalMouseListener gml = new GlobalMouseListener(MouseEvent.MOUSE_CLICKED);
gml.mouseLocationProperty().addListener((obs, oldLocation, newLocation) -> {
double x = newLocation.getX();
double y = newLocation.getY();
gml.cleanup();
// restore window...
pointProcessor.accept(x, y);
});
mainGUI.minimizeGUI();
}
and then each of your button handlers just reduce to
@FXML
public void handleGetPos() {
processMouseLocation((x, y) -> {
// code to handle mouse press at x,y...
});
}
That's all untested (both compiling and running, I just typed it in here) but I think it should be enough to get you started. The point is you just expose a property for the mouse location, and listen to it from each button handler, executing the button-specific code when it changes. Your native mouse listener just has to update the property when the relevant event occurs; it doesn't need a reference to the controller, etc.
Upvotes: 1