I think to do this, you need to create a generic model Parameter class, and use that as the type for your table. You can make it abstract and define an abstract getEditor() method (or perhaps delegate the editor factory to another class, but I will try to keep this as simple as possible). Then define subclasses which create different editors as required.
This might look something like this:
public abstract class Parameter<T> {
private final BooleanProperty editable = new SimpleBooleanProperty();
private final ObjectProperty<T> value = new SimpleObjectProperty<>();
private final String name ;
public Parameter(String name, T value, boolean editable) {
this.name = name ;
setValue(value);
setEditable(editable);
}
public Parameter(String name, T value) {
this(name, value, true);
}
public String getName() {
return name ;
}
public ObjectProperty<T> valueProperty() {
return value ;
}
public T getValue() {
return valueProperty().get();
}
public void setValue(T value) {
valueProperty().set(value);
}
public BooleanProperty editableProperty() {
return editable ;
}
public boolean isEditable() {
return editableProperty().get() ;
}
public void setEditable(boolean editable) {
editableProperty().set(editable);
}
public abstract Node getEditor() ;
}
Then you might have a simple implementation like this for "free" strings:
public class StringParameter extends Parameter<String> {
private final TextField editor ;
public StringParameter(String name, String value) {
super(name, value);
editor = new TextField();
editor.textProperty().bindBidirectional(valueProperty());
}
@Override
public Node getEditor() {
return editor ;
}
}
and maybe something like this for a spinner:
public class BoundIntegerParameter extends Parameter<Integer> {
private final Spinner<Integer> editor ;
public BoundIntegerParameter(int min, int max, String name, int value) {
super(name, value);
editor = new Spinner<>(min, max, value);
editor.setEditable(true);
editor.getValueFactory().valueProperty().bindBidirectional(valueProperty());
}
@Override
public Node getEditor() {
return editor ;
}
}
For the "fixed list" you could similarly implement a FixedStringParameter which took a list of strings, and whose getEditor method returned a ComboBox. Another approach for fixed choices might be to use Enum types: this might look like
public class EnumParameter<E extends Enum<E>> extends Parameter<E> {
private final ComboBox<E> editor ;
public EnumParameter(String name, E value) {
super(name, value);
editor = new ComboBox<>();
@SuppressWarnings("unchecked")
Class<E> type = (Class<E>) value.getClass();
E[] values = type.getEnumConstants() ;
editor.getItems().setAll(values);
editor.valueProperty().bindBidirectional(valueProperty());
}
@Override
public Node getEditor() {
return editor ;
}
}
Now for the cell implementation for the value column, you need a bit of trickery. This seems to work:
public class ParameterValueEditingCell extends TableCell<Parameter<?>, Object> {
@Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
setText(null);
Parameter<?> param = getTableView().getItems().get(getIndex());
setGraphic(param.getEditor());
} else {
setText(item.toString());
setGraphic(null);
}
}
}
@Override
public void startEdit() {
// check if current parameter is editable and bail if not:
int index = getIndex();
if (index < 0 || index >= getTableView().getItems().size()) {
return ;
}
if (! getTableView().getItems().get(index).isEditable()) {
return ;
}
super.startEdit();
setText(null);
setGraphic(getTableView().getItems().get(getIndex()).getEditor());
}
@Override
public void cancelEdit() {
super.cancelEdit();
Object item = getItem();
setText(item == null ? null : item.toString());
setGraphic(null);
}
}
Finally, you can set things up as
TableView<Parameter<?>> table = new TableView<>();
table.setEditable(true);
TableColumn<Parameter<?>, Object> valueColumn = new TableColumn<>("Value");
// I can't see any way to set this up without the ugly (unchecked) cast
// Any ideas?
valueColumn.setCellValueFactory(cellData -> (ObservableValue<Object>)cellData.getValue().valueProperty());
valueColumn.setCellFactory(tc -> new ParameterValueEditingCell());
I made a complete example here
TableCellimplementation. Depending on the exact requirements (e.g. if and when the "editable" property and the list of allowed values for any given item can change), it might be fairly straightforward, or quite complex.