Equivalent of FocusEvent.getOppositeComponent in JavaFx - java

In my JavaFx application, I want to call a method when the main frame gains focus. However, I want to react only in the case where the focus was outside my application and came back (not when a dialog closes for example).
When the application was in Swing, I could use the method
FocusEvent.getOppositeComponent
(which corresponds to the element that lost focus), and if it was null I knew the focus was previously outside my application.
I have not found any equivalent in JavaFX.
I have tried looking at window events, by adding an event filter on my window:
primaryStage.addEventFilter(Event.ANY, e -> System.out.println("event " + e));
but it doesn't track focus events.

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.

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:
w1 receives event WINDOW_HIDING
w2 focusProperty changes to true
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:
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))

Related

Combobox action get's called when programmatically clearing selection

I am using this piece of code to try and remove the item that get's selected from a combobox and I want to do something further with it, but the problem is that the value selected should be removed from the list. The following is what I am using to fill the dropdown
unpickedRoles = FXCollections.observableArrayList();
rollenDropdown.setItems(unpickedRoles);
unpickedRoles.addAll(Rol.DIRECTIE, Rol.MANAGER, Rol.MVOC, Rol.STAKEHOLDER);
#FXML
private void selectRol(ActionEvent event) {
Rol selected = rollenDropdown.getSelectionModel().getSelectedItem();
if (selected != null) {
rollenDropdown.getSelectionModel().clearSelection();
rollenDropdown.getItems().remove(selected);
}
}
Now whenever the code get's called when a selection is made apart from the first one, the code seems to get's recalled internally by the rollenDropdown.getSelectionModel().clearSelection(); function, how could I remove the selection without the javafx action getting recalled? And how come this only happens when not selecting the first one?
EDIT: It probably has something to do with an item getting deselected thus recalling the method
EDIT 2: Adding a null check does not help
Kind regards
Jasper
If you are concerned about the unspecified time at which Platform.runLater executes the action, you can try the below approach.
This approach is effectively using the AnimationTimer to run the desired actions at the end of the current pulse.
Considering its possible performance issues (mentioned in the doc),I would prefer to use Platform.runLater only in multi threading situations.
#FXML
private void selectRol(ActionEvent event) {
Rol selected = rollenDropdown.getSelectionModel().getSelectedItem();
if (selected != null) {
doEndOfPulse(() -> {
rollenDropdown.setValue(null);
rollenDropdown.getItems().remove(selected);
});
}
}
/**
* Executes the provided runnable at the end of the current pulse.
*
* #param runnable runnable to execute
*/
public static void doEndOfPulse(final Runnable runnable) {
new AnimationTimer() {
#Override
public void handle(final long now) {
runnable.run();
stop();
}
}.start();
}
According to How to remove selected elements from a ComboBox I had to use Platform.runLater() in order to make sure that the property change is dealt with before doing anything with the selection. This is the working code:
#FXML
private void selectRol(ActionEvent event) {
Rol selected = rollenDropdown.getSelectionModel().getSelectedItem();
if (selected != null) {
Platform.runLater(() -> {
rollenDropdown.setValue(null);
rollenDropdown.getItems().remove(selected);
});
}
}

JavaFX: Not in Application Thread only when updating a Label?

for the last few days I have been experimenting with JavaFX, FXML, Tasks and Properties. I stumbled upon a strange behaviour and hope that you can help me to understand better what's going on.
I have a minimalistic GUI which looks like this: GUI
If I click on the Button a new Task is created and started. This Task increments a Double Property and the new Value is written onto the Label and set in the ProgressBar. The Code of the Task can be seen here:
public class TestTask extends Task<Void>{
private final DoubleProperty doubleValue = new SimpleDoubleProperty();
#Override
protected Void call() throws Exception {
for(double i = 0.1; i <= 1; i = i+0.1 ) {
doubleValue.set(i);
Thread.sleep(1000);
}
return null;
}
public DoubleProperty test() {
return doubleValue;
}
}
The code of the FXML Controller is the following:
public class FXMLDocumentController implements Initializable {
#FXML private Label label;
#FXML private Slider slider;
//Called when the Button is clicked
#FXML
private void handleButtonAction(ActionEvent event) {
TestTask task = new TestTask();
task.test().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
//Works without problems
slider.setValue(newValue.doubleValue());
//Throws an exception
label.setText("Value" + newValue.doubleValue());
});
new Thread(task).start();
}
}
If I run this code the attempt to update the Label results in the following exception: java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4. The update of the Slider works fine and throws no exptions.
After reading the following questions on SO:
On which thread JavaFX change listeners are executed ?
Using threads to make database requests
I understand that the update of the Label fails because I'm trying to execute an UI Update within the Listener which is called from the TestTask Thread and not from the FXApplication Thread.
My question is now: Why does the Update of the Slider work und doesn't throw an exception? The update is carried out within the Listener and therefore from within the TestTask Thread. Shouldn't this attempt also throw a "Not on FX application thread" exception?
Thanks in advance for taking your time to help me.
Updating a property on one thread while you listen for changes to the property on another thread is just a race condition. Don't do it.
Not only does the JavaFX system assume updates to the UI occur on a single thread, it also assumes the same for properties, regardless of whether they are bound to UI elements. Don't rely on the JavaFX system to check for all race conditions and report and handle them correctly for you, because it is not designed to do that.
You need write your code in such a way as to prevent such conditions.

Using same EventHandler for MouseEvent & KeyEvent in JavaFX?

I'm new to Java programming, so this question may sound stupid to many here. I'm trying to get myself comfortable with JavaFX Event handling mechanism.
I'm developing a GUI where I want a button to perform the same function when it's clicked and also when the Enter key is pressed.
Can I do the following?
public class ButtonHandler implements EventHandler<ActionEvent>
{
somefunction();
}
And then use it for both KeyEvent & MouseEvent
button.setOnMouseClicked(new ButtonHandler);
button.setOnKeyPressed(new ButtonHandler);
As long as you don't need any information from the specific event (such as coordinates of the mouse, or the key that was pressed), you can do
EventHandler<Event> handler = event -> {
// handler code here...
};
and then
button.addEventHandler(MouseEvent.MOUSE_CLICKED, handler);
button.addEventHandler(KeyEvent.KEY_PRESSED, handler);
Of course, you can also just delegate the actual work to a regular method:
button.setOnMouseClicked(e -> {
doHandle();
});
button.setOnKeyPressed(e -> {
doHandle();
});
// ...
private void doHandle() {
// handle event here...
}

Building Multithreading ProgressBar in JavaFx

I am attempting to build a progress bar that is being updated as my application is retrieving and populating data to the GUI. I figured that the progress bar will be reused a lot so I decided to create its own class. However, I don't believe I understand either the Worker/Task or Multi-Threading in general to create a re-usable situation. What would be the recommended approach to creating a progress bar that can listen to the application thread and update the bar accordingly. Here is my attempt:
// Simple Progress Bar View as Pop Up
public class ProgressIndicatorUtil{
#FXML
private ProgressBar progressBar;
#FXML
private Label statusLabel;
#FXML
private Button closeButton;
#FXML
private Label valueLabel;
private Worker worker;
private Stage stage;
public void setPopUpStage(Stage stage) {
this.stage = stage;
}
public void setWorker(Worker worker) {
this.worker = worker;
}
public void setLinkToMainPage(Object controller) {
((Task<String>) worker).setOnSucceeded(event -> stage.close());
((Task<String>) worker).setOnCancelled(event -> {
closeButton.setVisible(true);
stage.requestFocus();
statusLabel.setTextFill(Color.RED);}
);
valueLabel.textProperty().bind(Bindings.format("%5.1f%%", worker.progressProperty().multiply(100)));
progressBar.progressProperty().bind(worker.progressProperty());
statusLabel.textProperty().bind(worker.messageProperty());
}
#FXML
private void handleClose(ActionEvent e){
stage.close();
}
}
The Controller that calls the View Pop-Up and runs the Progress Bar Thread.
public class MyController{
//Controller calling the view and disabling the main GUI
private void loadProgressBar(Worker worker){
try{
FXMLLoader loader = new FXMLLoader(getClass()
.getClassLoader().getResource("main/resources/fxml/ProgressBar.fxml"));
AnchorPane pane = (AnchorPane)loader.load();
Stage popUpStage = new Stage();
popUpStage.initModality(Modality.WINDOW_MODAL);
Scene scene = new Scene(pane);
popUpStage.setScene(scene);
ProgressIndicatorUtil controller = loader.getController();
controller.setPopUpStage(popUpStage);
controller.setWorker(worker);
controller.setLinkToMainPage(this);
mainPane.setDisable(true);
popUpStage.showingProperty().addListener((obs, hidden, showing) -> {
if(hidden) mainPane.setDisable(false);});
popUpStage.show();
}catch(IOException e){
e.printStackTrace();
}
}
private void runProgressBar(Worker worker) {
new Thread((Runnable) worker).start();
}
//A user action that runs the progress bar and GUI
#FXML
private void aBigProcessingEvent(ActionEvent event) {
Worker worker = new Task<String>(){
#Override
protected String call() throws Exception {
updateProgress(0, 3);
updateMessage("Clearing Data");
processingEvent01();
updateProgress(1, 3);
updateMessage("Retriving Data And Adding To List");
processingEvent02();
updateProgress(2, 3);
updateMessage("Populating Data");
processingEvent03();
updateProgress(3, 3);
return "Finished!";
}
};
loadProgressBar(worker);
runProgressBar(worker);
}
}
The program works fine, visually, but it throws an Exception like this (Not On FX Application Thread) and running Platform.runLater() on my "processingEvent" methods will cause my progress bar to be 100% immediately, but it won't throw anymore Exceptions. Any suggestion to how to split the application modification methods and the worker methods apart while keeping the progression connected to the processingEvent methods? Much thanks.
There is nothing wrong with the (incomplete) code you have posted, so there errors are in other parts of your code. Since the code is incomplete, I have to make some educated guesses as to what is happening. (Note: it is actually much better if you can create complete examples when you post questions, so that you ensure the cause of the issue you are asking about is included.)
Since you are getting an IllegalStateException "Not on the FX Application Thread", you must be updating the UI from a background thread. Since the only code you've posted that runs in a background thread is in the Task you create in aBigProcessingEvent(), the UI updates must be happening in the one or more of the processingEventXX() methods you haven't shown.
If you wrap the calls to processingEventXX() in Platform.runLater(), then you won't see any progressive updates to the progress bar. Platform.runLater() schedules the runnable you provide to be executed on the FX Application Thread and exits immediately. There is no other code in the Task that takes time to run, so the entire task is completed in very little time, and by the time the FX Application Thread renders the next frame, the task is complete and the progress property is at 1.
So presumably your processingEventXX() methods take time to execute, and also update the UI. You must wrap the calls that update the UI in those methods in Platform.runLater(...). The code wrapped in Platform.runLater(...) must not include code that takes a long time to run. I.e. they should look like
private void processingEvent01() {
// some long-running process here...
Platform.runLater(() -> {
// update UI here....
});
// some other long-running process here (perhaps)
}

javafx: when the completion of a drag and drop operation triggers a "long" task, the d&d icon persists on the target

As in the title, the user drops a file on the target. This should trigger an operation that is not quite immediate , say it takes 4 seconds.
The problem is that the icon used by the system to represent the dragged item stays on the window till the operation is completed and the void handle(DragEvent d) event returns.
This is perceived by the user as an application "freezing". We know that the application ever takes 4 seconds to process the dropped item, but this would probably not be noticed at all by the user if the icon disappeared immediately after the user releases the item on the target and before the 4 seconds operation started.
Here is the code relevant for dropping:
s.setOnDragDropped(new EventHandler<DragEvent>() {
#Override public void handle(DragEvent t) {
//1. The drop is OK
t.setDropCompleted(true);
//<--I THINK THAT SOMETHING SHOULD BE PUT HERE TO FORCE D&D TO REMOVE THE ICON
//2. let's start an elaboration that involves the files
// that have been dropped!
try { Thread.sleep(4000); } catch (InterruptedException ignore_exception_in_test) {}
}
});
Are there any options to address this problem?
The complete code, if you just want to reproduce the case; just fix imports and drop a file from Explorer into the stage.
public class ShortSelfCompilableExample01 extends Application {
Stage stage;
#Override public void start(Stage stage) throws Exception {
stage.setTitle("Drag&Drop test");
this.stage = stage;
Scene s = new Scene(new BorderPane());
stage.setScene(s);
s.setOnDragDropped(new EventHandler<DragEvent>() {
#Override public void handle(DragEvent t) {
//1. Ok, the drop is OK
t.setDropCompleted(true);
//2. let's start an elaboration that involves the files
// that have been dropped!
try {
Thread.sleep(6000);
} catch (InterruptedException ignore_exception_in_test_environments) {}
}
});
s.setOnDragOver(new EventHandler<DragEvent> () {
#Override public void handle(DragEvent t) {
t.acceptTransferModes(TransferMode.ANY);
}
});
stage.show();
}
}
PS. Why I didn't use a thread
The "first class" solution for responsive UIs is to use a thread.
But this is not a background task with very long execution time. This should last in the worst case 5 seconds, while threading introduces a certain degree of complexity.
The shorter is the lenght of the operation, the less is convenient introduce a thread just to "smooth" the UI.
Why this happens: the problem is in the design of the DnD. It depends on the platform, but in general the OS invokes some method which is that propagates up to JavaFX and ends up in your event handler. This method should return a result of the DnD operation so that the OS could know what to do. So it is synchronous by design.
What to do: your proposal with using another thread is probably the only way to handle this problem. It should be quite simple: get all the info needed for the long operation in the event handler, start a new thread, make the long operation there and update the UI using Platform.runLater

Categories