us.ihmc.javafx.parameter.StoredPropertyTableViewWrapper Maven / Gradle / Ivy
The newest version!
package us.ihmc.javafx.parameter;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.util.Callback;
import javafx.util.StringConverter;
import us.ihmc.tools.property.*;
import javax.swing.*;
import java.io.File;
import java.nio.file.Path;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
public class StoredPropertyTableViewWrapper
{
// total cell width
private final double cellWidth;
// amount of cell width for the parameter's label
private final double cellLabelWidth;
private final int tableColumns;
private final TableView parameterTable;
private final HashMap controlMap = new HashMap<>();
private final ObservableList parameterTableRows = FXCollections.observableArrayList();
private final JavaFXStoredPropertyMap javaFXStoredPropertyMap;
public StoredPropertyTableViewWrapper(double cellWidth,
double cellLabelWidth,
int tableColumns,
TableView parameterTable,
JavaFXStoredPropertyMap javaFXStoredPropertyMap,
int numberOfExtraRows)
{
this.cellWidth = cellWidth;
this.cellLabelWidth = cellLabelWidth;
this.tableColumns = tableColumns;
this.parameterTable = parameterTable;
this.javaFXStoredPropertyMap = javaFXStoredPropertyMap;
setup(numberOfExtraRows);
}
private void setup(int numberOfExtraRows)
{
parameterTable.setPadding(new Insets(10.0));
StoredPropertyKeyListReadOnly keys = javaFXStoredPropertyMap.getStoredPropertySet().getKeyList();
int numRows = keys.keys().size() / tableColumns + 1;
List> orderedKeys = new ArrayList<>(keys.keys());
Comparator> nameSorter = Comparator.comparing(StoredPropertyKey::getTitleCasedName, String.CASE_INSENSITIVE_ORDER);
orderedKeys.sort(nameSorter);
// create all the controls up front
for (StoredPropertyKey> propertyKey : orderedKeys)
{
controlMap.put(propertyKey, createEditor(propertyKey));
}
javaFXStoredPropertyMap.bindStoredToJavaFXUserInput();
parameterTableRows.clear();
// TableView sometimes doesn't show final row, add extra at the bottom...
// make a parameter if this is too much, but for 2 rows it would still sometimes not render the bottom
for (int row = 0; row < numRows + numberOfExtraRows; row++)
{
ParametersTableRow tableRow = new ParametersTableRow();
for (int col = 0; col < tableColumns; col++)
{
int index = tableColumns * row + col;
if (index < orderedKeys.size())
{
tableRow.parameters.add(new ParameterTableCell(orderedKeys.get(index)));
}
else
{
tableRow.parameters.add(new ParameterTableCell());
}
}
parameterTableRows.add(tableRow);
}
parameterTable.setItems(parameterTableRows);
for (int i = 0; i < tableColumns; i++)
{
final int columnIndex = i;
TableColumn column = new TableColumn<>();
column.setPrefWidth(cellWidth);
column.setCellFactory(new ParameterCellFactory());
column.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().parameters.get(columnIndex)));
parameterTable.getColumns().add(column);
}
parameterTable.refresh();
}
public void setTableUpdatedCallback(Runnable tableUpdateCallback)
{
javaFXStoredPropertyMap.addAnyJavaFXValueChangedListener(tableUpdateCallback);
}
/**
* Should be called after table is loaded, after Stage.show() is called on primary stage
*/
public void removeHeader()
{
Pane header = (Pane) parameterTable.lookup("TableHeaderRow");
if (header.isVisible())
{
header.setMaxHeight(0);
header.setMinHeight(0);
header.setPrefHeight(0);
header.setVisible(false);
}
parameterTable.setSelectionModel(null);
}
/**
* Loads new file. Subsequent saves will write to the selected file
*/
public void loadNewFile()
{
JFileChooser fileChooser = new JFileChooser();
Path saveFileDirectory = javaFXStoredPropertyMap.getStoredPropertySet().findSaveFileDirectory();
fileChooser.setCurrentDirectory(saveFileDirectory.toFile());
int chooserState = fileChooser.showOpenDialog(null);
if (chooserState == JFileChooser.APPROVE_OPTION)
{
File selectedFile = fileChooser.getSelectedFile();
String fileName = selectedFile.getName();
javaFXStoredPropertyMap.getStoredPropertySet().load(fileName, false);
javaFXStoredPropertyMap.copyStoredToJavaFX();
}
}
public class ParametersTableRow
{
private final List parameters = new ArrayList<>();
}
public class ParameterTableCell
{
private final StoredPropertyKey> parameter;
// for empty cell
public ParameterTableCell()
{
parameter = null;
}
public ParameterTableCell(StoredPropertyKey> parameter)
{
this.parameter = parameter;
}
boolean isEmpty()
{
return parameter == null;
}
}
public class ParameterCellFactory implements Callback, TableCell>
{
@Override
public TableCell call(TableColumn param)
{
return new TableCell()
{
final HBox hBox = new HBox();
final Label label = new Label();
@Override
protected void updateItem(ParameterTableCell tableCell, boolean empty)
{
super.updateItem(tableCell, empty);
hBox.getChildren().clear();
if (tableCell != null && !tableCell.isEmpty())
{
StoredPropertyKey> propertyKey = tableCell.parameter;
hBox.getChildren().add(label);
label.setPrefWidth(cellLabelWidth);
label.textProperty().setValue(propertyKey.getTitleCasedName());
Region region = new Region();
HBox.setHgrow(region, Priority.ALWAYS);
hBox.getChildren().add(region);
Control spinner = controlMap.get(propertyKey);
spinner.setPrefWidth(cellWidth - cellLabelWidth);
hBox.getChildren().add(spinner);
}
setGraphic(hBox);
}
};
}
}
private Control createEditor(StoredPropertyKey> propertyKey)
{
Control control;
if (propertyKey instanceof BooleanStoredPropertyKey)
{
CheckBox checkBox = new CheckBox("");
javaFXStoredPropertyMap.put(checkBox, (BooleanStoredPropertyKey) propertyKey);
checkBox.setAlignment(Pos.CENTER);
control = checkBox;
}
else if (propertyKey instanceof DoubleStoredPropertyKey)
{
Spinner spinner = new Spinner<>(-Double.MAX_VALUE, Double.MAX_VALUE, 0.0, 0.1);
spinner.setEditable(true);
spinner.getEditor().setTextFormatter(new TextFormatter<>(new DoubleStringConverter()));
spinner.getValueFactory().setConverter(new DoubleStringConverter());
javaFXStoredPropertyMap.put(spinner, (DoubleStoredPropertyKey) propertyKey);
control = spinner;
}
else if (propertyKey instanceof IntegerStoredPropertyKey)
{
Spinner spinner = new Spinner<>(Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 1);
spinner.setEditable(true);
javaFXStoredPropertyMap.put(spinner, (IntegerStoredPropertyKey) propertyKey);
control = spinner;
}
else
{
throw new RuntimeException("Unknown parameter property: " + propertyKey.getClass());
}
return control;
}
private static final NumberFormat numberFormat = NumberFormat.getInstance();
static
{
numberFormat.setMinimumIntegerDigits(1);
numberFormat.setMinimumFractionDigits(3);
numberFormat.setMaximumFractionDigits(8);
}
private static class DoubleStringConverter extends StringConverter
{
@Override
public String toString(Double object)
{
if (object == null)
return numberFormat.format(0.0);
else if (!Double.isFinite(object))
return Double.toString(object);
else
return numberFormat.format(object);
}
@Override
public Double fromString(String string)
{
return Double.parseDouble(string);
}
}
}