Equivalent of FocusEvent.getOppositeComponent in JavaFx

I finally found a semi-satisfactory way of handling the problem, using the order of the events in JavaFX, so I'm posting it as an answer in case it can help others.

When a window w1 closes, giving focus to a window w2, the event order is as follow:

  1. w1 receives event WINDOW_HIDING
  2. w2 focusProperty changes to true
  3. w1 receives event WINDOW_HIDDEN

So I wrote the following code to allow me to know whether the focus comes from an internal window:

public class MainStage {
    private Stage primaryStage;
    private AtomicBoolean triggerEventOnFocusGain = new AtomicBoolean(true);

    ...

    primaryStage.focusedProperty.addListener((prop, oldVal, newVal) -> {
        if(newVal.booleanValue() && triggerEventOnFocusGain.get()) {
            doStuff();
        }
    });
}

public class SomeDialog {
    private MainStage mainStage;
    private Window dialogWindow;

    ...

    dialogWindow.addEventHandler(WindowEvent.WINDOW_HIDING, event ->
        mainStage.setTriggerEventOnFocusGain(false));
    dialogWindow.addEventHandler(WindowEvent.WINDOW_HIDDEN, event ->
        mainStage.setTriggerEventOnFocusGain(true));
}

The only issue is that I have to do that for all internal windows/dialogs.

In my case I eventually decided that I could get away doing that for only a handful of dialogs, for which them triggering the event would be problematic, and ignore the others.

The other way of course would be to introduce a common abstract parent of all my view classes that does the above code.


JavaFX hierarchy is based on: Stage -> Scene -> Nodes -> ... -> Nodes:

enter image description here

If you want to listen focus of Stage (window), you can add listener to Stage focused Property of Stage:

Stage stage = ...
stage.focusedProperty()
        .addListener((observable, oldValue, newValue) -> {
                    if (!stage.isFocused()) { 
                       //action
                    }
                }
        );

This doesn't solve the problem in the question. You can't tell here what component had the focus. oldValue and newValue are booleans, so your if is trivial

You can check that you all Stages lost focuses (implement custom ChangeListener):

class AllStageUnfocusedListener implements ChangeListener<Boolean>{
    //IdentitySet and Runnable use only as example
    private final Set<Stage> stageSet;
    private final Runnable runnable;

    public AllStageUnfocusedListener(Runnable runnable) {
        this.stageSet =  Collections.newSetFromMap(new IdentityHashMap<>());
        this.runnable =  runnable;
    }

    public ChangeListener<Boolean> add(Stage stage){
        stageSet.add(stage);
        return this;
    }

    @Override
    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
        if(isAllStageLostFocus()){
            runnable.run();
        }
    }

    private boolean isAllStageLostFocus() {
        for (Stage stage : stageSet) {
            if (stage.isFocused()) {
                return false;
            }
        }
        return true;
    }
} 

and add Listener to Focused Property:

AllStageUnfocusedListener changeListener = new AllStageUnfocusedListener(() -> { /* action */ });
Stage stage = ...
stage.focusedProperty()
            .addListener(changeListener.add(stage))

There is no equivalent in JavaFX. Focus changes are handled as a boolean property for each window separately, so you can only tell if a window received or lost focus. If you register a listener to all windows in your application, you could tell if one of them lost focus when another gained it.

There is no "FocusEvent" in JavaFX, you can find all event types listed in Event.

You can request the feature here.

Tags:

Java

Focus

Javafx