I have a table as follows:
with(myTable)
{
enableCellEditing()
columnResizePolicy = SmartResize.POLICY
isEditable = true
column("Issue Date", TradeEntity::issueDate).makeEditable().cellFormat { tc ->
style { if (tc.dayOfMonth==10) backgroundColor += Color.ORANGE }
text = tc.toString()
}
I cannot make this editable AND having a style.
It's either I have a style, either it's editable...
Any help?
EDIT Solved, at least in Java, I guess it's all because I didn't call super.updateItem(it,empty) in Kotlin
cellFormat will replace the CellFactory installed when you call makeEditable(), so they are in direct competition with each other. For that reason, TornadoFX provides cellDecorator, which keeps the existing CellFactory:
column("First Name", Customer::firstNameProperty) {
makeEditable()
cellDecorator {
style {
fontWeight = FontWeight.BOLD
}
}
}
Ok I don't know why no answers, but I somehow managed to do it in Java thanks to 2013 Oracle tutorial https://docs.oracle.com/javafx/2/ui_controls/table-view.htm,and to Intellij autocompetion.
Now I have to do that in Kotlin
Callback<TableColumn<Person, String>, TableCell<Person, String>> cb = new Callback<TableColumn<Person, String>, TableCell<Person, String>>()
{
#Override
public TableCell<Person, String> call(TableColumn<Person, String> param) {
TextFieldTableCell res = new TextFieldTableCell<Person, String>(new DefaultStringConverter()) {
#Override
public void updateItem(String it, boolean empty) {
super.updateItem(it, empty);
if (it == null || empty) {
setText(null);
setStyle("");
} else {
setText(it);
setStyle("-fx-background-color: red");
}
}
};
System.out.println("callback called");
return res;
}
};
yourColumn.setCellFactory(cb);
Related
I have a TableView with an editable TextFieldTableCell that I want to restrict to be available based on a BooleanProperty of my model object.
For example, textField.disableProperty().bind(item.editableProperty().not())
Currently, I have the basic implementation from the Oracle docs:
colComment.setCellFactory(TextFieldTableCell.forTableColumn());
colComment.setOnEditCommit(event -> event.getTableView().getItems().get(
event.getTablePosition().getRow()).setComment(
event.getNewValue())
);
This obviously does not allow much flexibility. The desire is to check the item's editableProperty and if it is true, display the TextFieldTableCell and bind it to the item's commentProperty.
If that property is false, the cell should simply display the value of the commentProperty.
I have not worked with editable TableViews in the past so I am a bit lost.
I have tried to hack out a workaround with manually setting the graphic myself, but that just does nothing with the cell:
colComment.setCellFactory(cell -> new TableCell<LogEntry, String>() {
final TextField txtComment = new TextField();
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
LogEntry logEntry = (LogEntry) getTableRow().getItem();
if (logEntry.isEditable()) {
txtComment.textProperty().bindBidirectional(logEntry.commentProperty());
setGraphic(txtComment);
} else {
setText(item);
}
}
}
});
The basic approach is to disallow cell's editing based on a condition. TextFieldTableCell has no direct support for such, but can be extended just as any other type of cell. Options are
override startEdit to do nothing if the condition is not met
bind the cell's editability property to a condition of the rowItem
The most simple is the first (the latter is a bit more involved, due to requiring updates when parent TableRow and its item changes). A quick example (all boiler-plate except the cell ;):
public class TableCellConditionalEditable extends Application {
/**
* Cell with custom condition to prevent editing.
*/
public static class ConditionalEditableCell extends TextFieldTableCell<ConditionalWritable, String> {
public ConditionalEditableCell() {
super(new DefaultStringConverter());
}
/**
* Overridden to do nothing if rowItem-related condition not met.
*/
#Override
public void startEdit() {
if (!isConditionalEditable()) return;
super.startEdit();
}
private boolean isConditionalEditable() {
if (getTableRow() == null || getTableRow().getItem() == null || isEmpty()) return false;
return getTableRow().getItem().writableProperty().get();
}
}
private Parent createContent() {
TableView<ConditionalWritable> table = new TableView<>(ConditionalWritable.conditionalWritables());
TableColumn<ConditionalWritable, String> text = new TableColumn<>("Text");
text.setCellValueFactory(cc -> cc.getValue().textProperty());
TableColumn<ConditionalWritable, Boolean> writable = new TableColumn<>("Writable");
writable.setCellValueFactory(cc -> cc.getValue().writableProperty());
table.getColumns().addAll(text, writable);
table.setEditable(true);
text.setCellFactory(cc -> new ConditionalEditableCell());
BorderPane content = new BorderPane(table);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
public static class ConditionalWritable {
private SimpleStringProperty text;
private SimpleBooleanProperty writable;
public ConditionalWritable(String text, boolean writable) {
this.text = new SimpleStringProperty(text);
this.writable = new SimpleBooleanProperty(writable);
}
public StringProperty textProperty() {
return text;
}
public BooleanProperty writableProperty() {
return writable;
}
public static ObservableList<ConditionalWritable> conditionalWritables() {
return FXCollections.observableArrayList(
new ConditionalWritable("some data", false),
new ConditionalWritable("other data", true),
new ConditionalWritable("nothing important", true)
);
}
}
}
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
Hi I have a little problem in this Cell Factory :
private Callback<TableColumn<Member, String>,
TableCell<Member, String>> setPhotoCellFactory() {
Callback<TableColumn<Member, String>, TableCell<Member, String>> callback = new Callback<TableColumn<Member, String>, TableCell<Member, String>>() {
#Override
public TableCell<Member, String> call(TableColumn<Member, String> param) {
TableCell<Member, String> cell = new TableCell<Member, String>() {
#Override
protected void updateItem(String item, boolean empty) {
System.out.println(item);
super.updateItem(item, empty);
ImageView imageview = new ImageView();
imageview.setFitHeight(TABLE_IMAGE_MEMBER_HEIGHT);
imageview.setFitWidth(TABLE_IMAGE_MEMBER_WIDTH);
if(item != null) {
imageview.setImage(new Image(item));
setGraphic(imageview);
} else {
if(!empty) {
imageview.setImage(new Image(ImageFiles.GENERIC_MALE.toString()));
setGraphic(imageview);
} else {
setGraphic(null);
}
}
}
};
return cell;
}
};
return callback;
}
In Member object when Photo is null getter return Image corresponding a his gender. In this callback it seems that getter is not used.
How can I acces member property in this case ??
#EDIT
In Member class :
public String getPhoto() {
if (photo.getValue() != null && !photo.getValue().equals("")) {
return photo.get();
} else if (getGender().equals(Gender.F)) {
return ImageFiles.GENERIC_FEMALE.toString();
} else {
return ImageFiles.GENERIC_MALE.toString();
}
}
adding CellValueFactory :
this.simPhotoColumn.setCellValueFactory(data -> data.getValue().photoProperty());
OK I was found the solution. My error that I was searching how set image in CellFactory.
The response is to set custom CellValueFactory.
I was replaced this :
this.simPhotoColumn.setCellValueFactory(data -> data.getValue().photoProperty());
by :
this.memberPhoto.setCellValueFactory(getPhotoValueFactory());
private Callback<TableColumn.CellDataFeatures<Member, String>, ObservableValue<String>> getPhotoValueFactory() {
Callback<TableColumn.CellDataFeatures<Member, String>, ObservableValue<String>> callback = param -> new ReadOnlyObjectWrapper<>(param.getValue().getPhoto());
return callback;
}
I have method that make a TableColumn editable that I made according to this tutorial, with the two changes recommended in this question witch are changing focusedProperty with setOnAction and add binding. The method is like this:
public static <T> void setAutoCompleteTableColumn(TableColumn<T,String> column, BiConsumer<T, String> field, String type, BiConsumer handleUpdates, List autoComplete){
column.setCellFactory(param -> {
return new TableCell<T, String>(){
private TextField textField;
#Override
public void startEdit() {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField(){
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.setOnAction(event ->{
if(textField.getText() != ""){
commitEdit(textField.getText());
}
});
AutoCompletionBinding<T> binding = TextFields.bindAutoCompletion(textField,autoComplete);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
});
column.setOnEditCommit(event -> {
T model = event.getRowValue();
if (!event.getNewValue().isEmpty()) {
Cloner cloner = new Cloner();
final T oldModel = cloner.deepClone(model);
//Update the old value with the new value
field.accept(model, event.getNewValue());
//Retrieve changes
handleUpdates.accept(oldModel, model);
}
});
}
I use this method in the controller like this:
TableColumn<Purchase, String> nameColumn = new TableColumn<>();
setAutoCompleteTableColumn(nameColumn, (p,s) -> validatePurchaseEdit(p,s), "text", (o, n) -> updateTracker(o, n), appState.items);
I have a button click event that switch the TableView editable to false.
For some reason when this event is executed the current cell that I am editing does commit edit but an other cell(a random one) switches to edit mode. Can some one explain what is happening, and how can I make all cells commit edit?
I am writing a podcast player and have a list of files that I display.
The user can double click an entry and that files play. If they select another one. I don’t know what has been selected in the list. The idea is to highlight the file that is playing in a different color. With still keeping the ability to select another file (don’t want to lose that functionality). Most audio players work on the same principle.
I had a look at this JavaFX tableview colors and this changes the color of the row based on data. But if the data changes the row color do not refresh as the data changes.
I have a table with cell factory and based on the variable "isSelectedFile" I set the cell style.
If the isSelectedFile=True at load time all is ok but once I change the data while the rows are loaded into the tableview it doesn't refresh.
My cell factory is as follows:
entryDisplayNameColumn.setCellFactory(new Callback<TableColumn<PlaylistEntry, String>, TableCell<PlaylistEntry, String>>() {
#Override
public TableCell call(TableColumn p) {
TableCell cell = new TableCell<Task, Object>() {
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
TableRow currentRow = getTableRow();
PlaylistEntry ple = currentRow == null ? null : (PlaylistEntry) currentRow.getItem();
if (ple != null) {
clearPriorityStyle();
setPriorityStyle(ple.isSelectedFile());
}
}
#Override
public void updateSelected(boolean upd) {
super.updateSelected(upd);
System.out.println("is update");
}
private void clearPriorityStyle() {
ObservableList<String> styleClasses = getStyleClass();
styleClasses.remove("priorityLow");
styleClasses.remove("priorityMedium");
styleClasses.remove("priorityHigh");
}
private void setPriorityStyle(boolean activeValue) {
if (activeValue) {
getStyleClass().add("priorityLow");
} else {
getStyleClass().add("priorityMedium");
}
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
return cell;
}
});
My CSS:
.priorityLow {
-fx-control-inner-background: snow;
-fx-accent: derive(-fx-control-inner-background, -40%);
-fx-cell-hover-color: derive(-fx-control-inner-background, -20%);
}
.priorityMedium {
-fx-control-inner-background: #1d1d1d;
-fx-accent: derive(-fx-control-inner-background, -40%);
-fx-cell-hover-color: derive(-fx-control-inner-background, -20%);
}
try this its perfectly worked...
tablecol.setCellFactory(new Callback<TableColumn<CheckDo, String>, TableCell<CheckDo, String>>() {
#Override
public TableCell<CheckDo, String> call(TableColumn<CheckDo, String> p) {
return new TableCell<CheckDo, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty())
this.setStyle("-fx-background-color:red");
this.getTableRow().setStyle("-fx-background-color:red");
setText(item);
}
}
};