TreeCell key events not firing - javafx

I have TreeView control with CellFactory:
tVMain.setCellFactory(new Callback<TreeView<ITVItem>, TreeCell<ITVItem>>()
{
#Override
public TreeCell<ITVItem> call(TreeView<ITVItem> p)
{
return new TextFieldTreeCellImpl();
}
});
TextFieldTreeCellImpl:
private static class TextFieldTreeCellImpl extends TextFieldTreeCell<ITVItem>
{
private TextField textField;
public TextFieldTreeCellImpl()
{
this.setOnKeyPressed((e) -> {
startEdit();
});
}
public void startEdit()
{
super.startEdit();
if (textField == null)
{
textField = new TextField();
}
textField.setText(getString());
setText(null);
setGraphic(textField);
textField.selectAll();
textField.requestFocus();
}
#Override
public void cancelEdit()
{
super.cancelEdit();
setText(((ITVItem) getItem()).toString());
setGraphic(getTreeItem().getGraphic());
}
}
The problem is that the OnKeyPressed event never fires. I tried with OnKeyReleased but the result is the same - no event.
TextFieldTreeCellImpl also has OnMouseReleased events (not shown in code for brevity) which do work fine.
LAST INFO: When TextFieldTreeCellImpl is in edit mode (startEdit() has been called and textField is visible) the events are triggered. But when the editing is cancelled and the textField removed, events are not firing again.
How can I get the key events to fire in non editing mode?
EDIT:
In response to the comment from James_D: I also tried adding
this.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
//SOMETHING
});
instead of OnKeyReleased but that still does not work.

Related

JavaFx How to make checkbox visible in tableColumnCell using cellFactory

I want to add checkBox to tableCell but i dont know what to do to make checkBox visible when i use .setCellFactory
I want to achive this with cellFactory function ---> checkBoxVisible
when i want to make use of cellFactory checkBox are not visible
----> wrong effect
window1.setCellFactory(new BooleanColorCellFactory());
window2.setCellFactory(new BooleanColorCellFactory());
This is BooleanColorCellFactory Class
#Override
public TableCell<Server, CheckBox> call(TableColumn<Server, CheckBox> param) {
return new TableCell<Server, CheckBox>(){
#Override
protected void updateItem(CheckBox item, boolean empty) {
super.updateItem(item, empty);
// if(!empty) {
// setVisible(true);
// setEditable(true);
// getChildren().add(item);
// setText(item.toString());
// if(item.isSelected())
// setStyle(" -fx-background-color: Green");
// else
// setStyle(" -fx-background-color: RED");
}
// }
};
}
}
I have tried some things but nothing was working.
What i need to add in BooleanColorCellFactory to make this work ?
UPDATE:
So i was playing around and i manage to get step closer to solution by adding this into BooleanColorCellFactory Class
if(!getChildren().contains(item))
getChildren().add(item);
but it is buggy and dont looks well and its added after i start scrolling(which is weird behavior for me)--> click
You shouldn't put a Node inside the item class unless you really need to. Furthermore never access the children of a Control directly unless you're writing a Skin for this Control.
You should instead add a BooleanProperty to the Server class:
private final BooleanProperty window1 = new SimpleBooleanProperty();
public boolean isWindow1() {
return window1.get();
}
public void setWindow1(boolean value) {
window1.set(value);
}
public BooleanProperty window1Property() {
return window1;
}
TableColumn<Server, Boolean> window1;
Callback<TableColumn<Server, Boolean>, TableCell<Server, Boolean>> factory = new BooleanColorCellFactory();
// cellValueFactory returns property
window1.setCellValueFactory(new PropertyValueFactory<>("window1"));
window1.setCellFactory(factory);
...
window2.setCellFactory(factory);
#Override
public TableCell<Server, Boolean> call(TableColumn<Server, Boolean> param) {
return new TableCell<Server, Boolean>(){
private final CheckBox checkBox = new CheckBox();
{
checkBox.selectedProperty().addListener((o, oldValue, newValue) -> {
// save new value in row item
WritableValue<Boolean> property = (WritableValue<Boolean>) getTableColumn().getCellObservableValue​(getIndex());
property.setValue(newValue);
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
setStyle(null);
} else {
setGraphic(checkBox);
checkBox.setSelected(item);
setStyle(item ? "-fx-background-color: Green" : "-fx-background-color: RED");
}
}
};
}
Procede accordingly for window2

Table cell combobox - action not performed

I'm applying the below cell factory to a column.
targetEnviroment.setCellFactory(new Callback<TableColumn<DevWorkTabBench, String>, TableCell<DevWorkTabBench, String>>() {
#Override
public TableCell<DevWorkTabBench, String> call(TableColumn<DevWorkTabBench, String> param) {
TableCell<DevWorkTabBench, String> cell = new TableCell<DevWorkTabBench, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
String status = null;
try {
status = getTableView().getItems().get(getIndex()).getObjectStatus();
} catch (IndexOutOfBoundsException ex) {
status = "";
}
if (status.equalsIgnoreCase("ReadyForDeployment")) {
ComboBox<String> comboBox = new ComboBox(environmentList);
comboBox.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
commitEdit(newValue);
}
});
comboBox.setOnShown(new EventHandler<Event>() {
#Override
public void handle(Event event) {
getTableView().edit(getIndex(), getTableColumn());
getTableView().getSelectionModel().select(getIndex());
}
});
comboBox.setValue(item);
setGraphic(comboBox);
} else {
setGraphic(null);
}
if (empty) {
setGraphic(null);
}
}
};
return cell;
}
});
When I change the status to the mentioned status, I get the look of ComboBox in that particular cell but the drop down does not occur. Even after multiple clicks no action seems to be performed on the combobox. I do not get any exception other than the handled one. Other columns are editable and performing task as expected.
I have no idea what is wrong here. Can anyone please help me.
Since you are always displaying the combo box in the (non-empty) cells, you don't really need to go into "editing" mode as the standard TextFieldTableCell etc does. Your implementation is more similar to the CheckBoxTableCell, which essentially bypasses the editing mechanism. From the documentation for that class:
Note that the CheckBoxTableCell renders the CheckBox 'live', meaning
that the CheckBox is always interactive and can be directly toggled by
the user. This means that it is not necessary that the cell enter its
editing state (usually by the user double-clicking on the cell). A
side-effect of this is that the usual editing callbacks (such as on
edit commit) will not be called. If you want to be notified of
changes, it is recommended to directly observe the boolean properties
that are manipulated by the CheckBox.
So your cell implementation behaves similarly: don't invoke edit(...) (which I think is messing things up) and don't rely on the commitEdit(...), cancelEdit() etc methods (which won't work as you're not in editing state), but just update the model class directly.
I can't test, since there isn't a MCVE to work from, so this might not work directly, but it should be enough to get you started toward something that will work.
targetEnviroment.setCellFactory(new Callback<TableColumn<DevWorkTabBench, String>, TableCell<DevWorkTabBench, String>>() {
#Override
public TableCell<DevWorkTabBench, String> call(TableColumn<DevWorkTabBench, String> param) {
TableCell<DevWorkTabBench, String> cell = new TableCell<DevWorkTabBench, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null) ;
} else {
String status = getTableView().getItems().get(getIndex()).getObjectStatus();
if (status.equalsIgnoreCase("ReadyForDeployment")) {
ComboBox<String> comboBox = new ComboBox(environmentList);
comboBox.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
//commitEdit(newValue);
getTableView().getItems().get(getIndex()).setObjectStatus(newValue);
}
});
comboBox.setValue(item);
setGraphic(comboBox);
} else {
setGraphic(null);
}
}
}
};
return cell;
}
});

JavaFX contextmenu not hiding

I'm trying to show a context menu only when there is something selected in the list view.
So I called hide in its on showing event. However, this is not working. The context menu still shows up. Is this a bug, or not its intended use? Because JavaFX api seems to suggest hide() is suppose to do this.
Anyway this is the code.
ContextMenu menu = new ContextMenu();
menu.setOnShowing(new EventHandler<WindowEvent>() {
#Override
public void handle(final WindowEvent event) {
menu.hide();
}
});
It will probably work if you do
public void handle(final WindowEvent event) {
Platform.runLater(new Runnable() {
#Override
public void run() {
menu.hide();
}
});
}
but that really seems like a horrible hack.
Why not just set the context menu only if something is selected?
final ListView<T> listView = ... ;
final ContextMenu menu = new ContextMenu();
listView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<T>() {
#Override
public void changed(ObservableValue<? extends T> obs, T oldValue, T newValue) {
if (newValue == null) {
listView.setContextMenu(null);
} else {
listView.setContextMenu(menu);
}
}
});
(obviously replace T with whatever type your ListView is displaying).
private ContextMenu menu; private MenuItem deleteItem;
table.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {
#Override
public void handle(ContextMenuEvent event) {
if (table.getSelectionModel().getSelectedIndex() != -1) {
deleteItem.setVisible(true);
deleteItem.setText("delete: " + table.getSelectionModel().getSelectedItem().getName());
contextMenu.show(table, event.getScreenX(), event.getScreenY());
} else {
deleteItem.setVisible(false);
}
event.consume();
}
});
primaryStage.getScene().addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) { menu.hide(); }});

JavaFX editable ComboBox in a table view

I am using a editable ComboBox cell in a table view. Here is my ComboBox cell class
public class ComboBoxCell extends TableCell<ClassesProperty, String> {
private ComboBox<String> comboBox;
public ComboBoxCell() {
}
#Override
public void startEdit() {
super.startEdit();
if (comboBox == null) {
createComboBox();
}
setGraphic(comboBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
Platform.runLater(new Runnable() {
#Override
public void run() {
comboBox.requestFocus();
comboBox.getEditor().requestFocus();
comboBox.getEditor().selectAll();
}
});
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (comboBox != null) {
comboBox.setValue(getString());
}
setGraphic(comboBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createComboBox() {
// ClassesController.getLevelChoice() is the observable list of String
comboBox = new ComboBox<>(ClassesController.getLevelChoice());
comboBox.setEditable(true);
comboBox.setMinWidth(this.getWidth() - this.getGraphicTextGap()*2);
comboBox.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(comboBox.getSelectionModel().getSelectedItem());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
The problem I need to press three clicks on the table cell to get the text field of the combo box to edit the contents. Is there a way to make it in two clicks? I even used Platforn runlater but when I try to edit a cell at first time it takes three mouse clicks but at the second time only two clicks.
In your overridden cell.startEdit() method, Add a ListView with a textfield, then add this listview to setGraphic. This will show the listview directly once the row is selected and the cell is clicked, the listview will be inside the tablecell, I am yet to find a way to make it as a popup

setOnMouseEntered and setOnMouseExited foreverloop

I use a PopOver from controlsfx to display detail-data from a TableCell. I use a tableCellFactory to create individual PopOvers. I have a problem with this code:
public class PopupTableCell extends TableCell {
#Override
protected void updateItem(final Object item, boolean empty) {
super.updateItem(item, empty);
if (empty){
setText(null);
} //if
else {
setText(item.toString());
// PopUp Anzeigen.
final PopOver PopUp = new PopOver();
PopUp.setContentNode(new Label(item.toString()));
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
Point MouseLocation = MouseInfo.getPointerInfo().getLocation();
PopUp.show(PopupTableCell.this, MouseLocation.getX(), MouseLocation.getY());
System.out.println("entered");
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("exited");
PopUp.hide();
}
});
}
}
When I place the cursor over a cell, it begins firing the entered and the exited events forever (even with a non moving mouse cursor.
How can I achieve the wanted behavior:
Mouse inside table cell -> popover.show()
Mouse leaves table cell -> popover.hide()
This is how I solved the same problem for me.
owner.setOnMouseMoved(evt -> {
if (owner.getLayoutBounds().contains(evt.getX(), evt.getY())) {
if (!popOver.isShowing()) {
popOver.show(owner);
}
}
});
owner.setOnMouseExited(evt -> {
if (owner.getLayoutBounds().contains(evt.getX(), evt.getY())) {
if (!popOver.isShowing())
popOver.show(owner);
} else {
popOver.hide();
}
});

Resources