I have an issue with ComboBox
when I select same item with control key pressed . This is what happens :
as you can see in the gif , combobox
change values normally , but when control key is pressed , the selected item is null if the new value is the same as the previous one .
I am using javafx 23 in openjdk 23.0.1 ( Amazon coretto ) on a linux ubuntu mate 24.04.
This is my minimal reproducible example
public class App extends Application {
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage stage) throws IOException {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().setAll("one", "two");
comboBox.getSelectionModel().selectFirst();
Scene scene = new Scene(new StackPane(comboBox), 320, 240);
String title = null;
scene.setOnKeyPressed(
keyEvent -> stage.setTitle(keyEvent.isControlDown() ? "Control down" : ""));
scene.setOnKeyReleased(e -> stage.setTitle(""));
stage.setTitle(title);
stage.setScene(scene);
stage.show();
}
}
I have an issue with ComboBox
when I select same item with control key pressed . This is what happens :
as you can see in the gif , combobox
change values normally , but when control key is pressed , the selected item is null if the new value is the same as the previous one .
I am using javafx 23 in openjdk 23.0.1 ( Amazon coretto ) on a linux ubuntu mate 24.04.
This is my minimal reproducible example
public class App extends Application {
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage stage) throws IOException {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().setAll("one", "two");
comboBox.getSelectionModel().selectFirst();
Scene scene = new Scene(new StackPane(comboBox), 320, 240);
String title = null;
scene.setOnKeyPressed(
keyEvent -> stage.setTitle(keyEvent.isControlDown() ? "Control down" : ""));
scene.setOnKeyReleased(e -> stage.setTitle(""));
stage.setTitle(title);
stage.setScene(scene);
stage.show();
}
}
Share
Improve this question
asked Feb 6 at 4:49
Giovanni ContrerasGiovanni Contreras
2,5691 gold badge15 silver badges26 bronze badges
1
|
1 Answer
Reset to default 4I think this is intended behavior: to deselect the selection when ctrl key is pressed.
You can modify this behavior in two ways.
- by including an event filter on the cell (as mentioned by @Slaw)
- by including a custom behavior
Option 1 (event filter):
Include a mouse pressed event filter in the custom ListCell constructor, to consume the event when the shortcut is pressed and if the cell is already selected. The only side effect is you lose any mouse pressed events on the cell.
class MyListCell<T> extends ListCell<T> {
public MyListCell() {
addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
if (e.isShortcutDown() && isSelected()) {
e.consume();
}
});
}
}
Option 2 (custom behavior):
This is more like specifically tweaking the select functionality rather than blocking all mouse pressed handlers of the cell. The general idea is to create a
custom list cell/skin/behavior classes and tweak the doSelect method to ignore the shortcutDown
parameter when processing the selection. The behavior code will be something like below:
you may need to include the below VM argument
--add-exports javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED
class MyListCellBehavior<T> extends ListCellBehavior<T> {
public MyListCellBehavior(ListCell<T> control) {
super(control);
}
@Override
protected void doSelect(double x, double y, MouseButton button, int clickCount, boolean shiftDown, boolean shortcutDown) {
// We always pass the shortcutDown as false to ignore the inputs.
super.doSelect(x, y, button, clickCount, shiftDown, false);
}
}
The full working demo with both approaches is below:
import com.sun.javafx.scene.control.behavior.ListCellBehavior;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.Skin;
import javafx.scene.control.skin.ListCellSkin;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class CustomListCellBehaviour_Demo extends Application {
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage stage) {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().setAll("one", "two");
comboBox.getSelectionModel().selectFirst();
comboBox.setCellFactory(param -> new MyListCell<>());
Scene scene = new Scene(new StackPane(comboBox), 320, 240);
String title = null;
scene.setOnKeyPressed(
keyEvent -> stage.setTitle(keyEvent.isControlDown() ? "Control down" : ""));
scene.setOnKeyReleased(e -> stage.setTitle(""));
stage.setTitle(title);
stage.setScene(scene);
stage.show();
}
class MyListCell<T> extends ListCell<T> {
public MyListCell() {
// UNCOMMENT THE BELOW CODE FOR OPTION 1 APPROACH AND GET RID OF THE SKIN CLASSES
// addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
// if (e.isShortcutDown() && isSelected()) {
// e.consume();
// }
// });
}
@Override
protected Skin<?> createDefaultSkin() {
return new MyListCellSkin<T>(this);
}
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setText(item.toString());
} else {
setText(null);
}
}
}
class MyListCellSkin<T> extends ListCellSkin {
private MyListCellBehavior<T> behavior;
public MyListCellSkin(ListCell<T> control) {
super(control);
behavior = new MyListCellBehavior<>(control);
}
@Override
public void dispose() {
super.dispose();
if (behavior != null) {
behavior.dispose();
}
}
}
class MyListCellBehavior<T> extends ListCellBehavior<T> {
public MyListCellBehavior(ListCell<T> control) {
super(control);
}
@Override
protected void doSelect(double x, double y, MouseButton button, int clickCount, boolean shiftDown, boolean shortcutDown) {
// We always pass the shortcutDown as false to ignore the inputs.
super.doSelect(x, y, button, clickCount, shiftDown, false);
}
}
}
ComboBox
is displaying aListView
in the popup. When you shortcut+click a selected cell in a list view, that cell is deselected. The skin of the combo box listens to the list view's selection model to know when it needs to update the combo box's value. It apparently interprets clearing the list view's selection as needing to set the combo box's value to null. I don't know if this is intended behavior (I would say it's a bug, as you shouldn't be able to "deselect" items of a combo box). A workaround could be to add an event filter and consume the appropriate mouse events – Slaw Commented Feb 6 at 6:21