Currently JavaFX provides a feature to dropdown the comboBox on hitting F4. We want to disable that feature and process other functions for F4. On first instance I thought this is pretty straight forward. My idea is that I will add a key event filter and consume it when F4 is pressed.
But unfortunately that didnt worked !! Upon investigation, I noticed that there is a part of code in ComboBoxPopupControl to handle the key event, which is set as KeyEvent.ANY filter. The strange part is that they consume the event after showing/hiding.
The part of code is as below:
private void handleKeyEvent(KeyEvent ke, boolean doConsume) {
// When the user hits the enter or F4 keys, we respond before
// ever giving the event to the TextField.
if (ke.getCode() == KeyCode.ENTER) {
setTextFromTextFieldIntoComboBoxValue();
if (doConsume && comboBoxBase.getOnAction() != null) {
ke.consume();
} else {
forwardToParent(ke);
}
} else if (ke.getCode() == KeyCode.F4) {
if (ke.getEventType() == KeyEvent.KEY_RELEASED) {
if (comboBoxBase.isShowing()) comboBoxBase.hide();
else comboBoxBase.show();
}
ke.consume(); // we always do a consume here (otherwise unit tests fail)
}
}
This makes me totally helpless, as now I can no more control this part of event chain by merely consuming the filters/handlers. None of the below filters helped me to stop showing the dropdown.
comboBox.addEventFilter(KeyEvent.ANY, e -> {
if (e.getCode() == KeyCode.F4) {
e.consume(); // Didn't stopped showing the drop down
}
});
comboBox.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.F4) {
e.consume(); // Didn't stopped showing the drop down
}
});
comboBox.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.F4) {
e.consume(); // Didn't stopped showing the drop down
}
});
The only way I can stop it is to consume the event on its parent and not allowing to delegate to ComboBox. But this is definetly an overhead, with already tens of ComboBox(es) across the application and many more to come.
My question is: Why did they implemented a feature which is tightly integrated not allowing the user to disable it?
Is there any alternate that I can implement on ComboBox level to stop showing/hiding the dropdown when F4 is pressed.
I tried the below approach to make it work. But I am not sure how much i can rely on Timeline based solution :(
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ComboBoxF4_Demo extends Application {
Timeline f4PressedTimeline = new Timeline(new KeyFrame(Duration.millis(100), e1 -> {
}));
@Override
public void start(Stage stage) throws Exception {
HBox root = new HBox();
root.setSpacing(15);
root.setPadding(new Insets(25));
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 600, 600);
stage.setScene(scene);
final ComboBox<String> comboBox = new ComboBox<String>() {
@Override
public void show() {
if (f4PressedTimeline.getStatus() != Animation.Status.RUNNING) {
super.show();
}
}
};
comboBox.setItems(FXCollections.observableArrayList("One", "Two", "Three"));
comboBox.addEventFilter(KeyEvent.ANY, e -> {
if (e.getCode() == KeyCode.F4) {
if (e.getEventType() == KeyEvent.KEY_RELEASED) {
f4PressedTimeline.playFromStart();
}
}
});
// NONE OF THE BELOW FILTERS WORKED :(
/*comboBox.addEventFilter(KeyEvent.ANY, e -> {
if (e.getCode() == KeyCode.F4) {
e.consume(); // Didn't stopped showing the drop down
}
});
comboBox.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.F4) {
e.consume(); // Didn't stopped showing the drop down
}
});
comboBox.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.F4) {
e.consume(); // Didn't stopped showing the drop down
}
});
*/
root.getChildren().addAll(comboBox);
stage.show();
}
}