2

I have a TextField, and I would like to do something if the user clicks anywhere that is not the TextField itself.

  • Apparently, the onMouseClicked event won't trigger if you don't click the node itself, so that wouldn't work.
  • Listening the focusedProperty may have been a good idea, but the problem is that almost the entirety of my application is not focus traversable, so in many cases clicking outside the textfield won't unfocus it, so the listener won't be notified.

The only thing left in my mind is to put an event filter on the scene itself, intercept mouse clicks, get the click coordinates and determine if they fall within the bounds of the textfield. I find this a little bit overkill and I may be missing something more obvious.

Is there another way to determine if the user clicked anywhere outside my TextField node?

Saturn
  • 17,888
  • 49
  • 145
  • 271

2 Answers2

8

The only thing left in my mind is to put an event filter on the scene itself, intercept mouse clicks, get the click coordinates and determine if they fall within the bounds of the textfield. I find this a little bit overkill and I may be missing something more obvious.

In fact I consider this your best option, but with a variation:

Use the PickResult provided by the MouseEvent to get the target Node and check, if it's in the hierarchy of the Node. (This can be necessary, if the Node has children; e.g. a TextField also contains a Text element.)

In fact this seems to be the only option that cannot be broken by consuming the event somewhere in the scene graph.

Example

This moves the focus to the root pane in case the user clicks somewhere except the TextField.

public static boolean inHierarchy(Node node, Node potentialHierarchyElement) {
    if (potentialHierarchyElement == null) {
        return true;
    }
    while (node != null) {
        if (node == potentialHierarchyElement) {
            return true;
        }
        node = node.getParent();
    }
    return false;
}

@Override
public void start(Stage primaryStage) {
    TextField textField = new TextField();
    textField.setMinSize(400, Region.USE_PREF_SIZE);
    textField.setMaxWidth(400);
    textField.setEditable(false);
    textField.textProperty().bind(Bindings.when(textField.focusedProperty()).then("Got the Focus!").otherwise("Please give me the focus!"));

    StackPane root = new StackPane();
    root.getChildren().add(textField);

    Scene scene = new Scene(root, 500, 200);

    scene.addEventFilter(MouseEvent.MOUSE_CLICKED, evt -> {
        if (!inHierarchy(evt.getPickResult().getIntersectedNode(), textField)) {
            root.requestFocus();
        }
    });

    primaryStage.setScene(scene);
    primaryStage.show();
}
Community
  • 1
  • 1
fabian
  • 80,457
  • 12
  • 86
  • 114
0

Assume you top level container of your app is Pane. You can apply mouse click event to that pane and inside it handle the mouse click event for the textfield. So this way, if the user clicks on the textfield or clicks somewhere else both events will be notified. Here's is the sample code.

Pane.setOnMouseClicked((MouseEvent evt) -> {
    System.out.println("Click outside textfield");

    textField.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent event) {
            System.out.println("textfield clicked");
        }
    });
});

If you have any other controls in that Pane. This mouse click wont interfere with them, it will be only called when you click directly on the pane.

Harshita Sethi
  • 2,035
  • 3
  • 24
  • 46