package com.kodcu.component; import javafx.beans.property.ObjectProperty; import javafx.collections.ObservableList; import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.input.KeyCode; import javafx.scene.layout.HBox; import javafx.util.StringConverter; public class CellUtils { static int TREE_VIEW_HBOX_GRAPHIC_PADDING = 3; /*************************************************************************** * * * Private fields * * * **************************************************************************/ private final static StringConverter<?> defaultStringConverter = new StringConverter<Object>() { @Override public String toString(Object t) { return t == null ? null : t.toString(); } @Override public Object fromString(String string) { return (Object) string; } }; private final static StringConverter<?> defaultTreeItemStringConverter = new StringConverter<TreeItem<?>>() { @Override public String toString(TreeItem<?> treeItem) { return (treeItem == null || treeItem.getValue() == null) ? "" : treeItem.getValue().toString(); } @Override public TreeItem<?> fromString(String string) { return new TreeItem<>(string); } }; /*************************************************************************** * * * General convenience * * * **************************************************************************/ /* * Simple method to provide a StringConverter implementation in various cell * implementations. */ @SuppressWarnings("unchecked") static <T> StringConverter<T> defaultStringConverter() { return (StringConverter<T>) defaultStringConverter; } /* * Simple method to provide a TreeItem-specific StringConverter * implementation in various cell implementations. */ @SuppressWarnings("unchecked") static <T> StringConverter<TreeItem<T>> defaultTreeItemStringConverter() { return (StringConverter<TreeItem<T>>) defaultTreeItemStringConverter; } private static <T> String getItemText(Cell<T> cell, StringConverter<T> converter) { return converter == null ? cell.getItem() == null ? "" : cell.getItem().toString() : converter.toString(cell.getItem()); } static Node getGraphic(TreeItem<?> treeItem) { return treeItem == null ? null : treeItem.getGraphic(); } /*************************************************************************** * * * ChoiceBox convenience * * * **************************************************************************/ static <T> void updateItem(final Cell<T> cell, final StringConverter<T> converter, final ChoiceBox<T> choiceBox) { updateItem(cell, converter, null, null, choiceBox); } static <T> void updateItem(final Cell<T> cell, final StringConverter<T> converter, final HBox hbox, final Node graphic, final ChoiceBox<T> choiceBox) { if (cell.isEmpty()) { cell.setText(null); cell.setGraphic(null); } else { if (cell.isEditing()) { if (choiceBox != null) { choiceBox.getSelectionModel().select(cell.getItem()); } cell.setText(null); if (graphic != null) { hbox.getChildren().setAll(graphic, choiceBox); cell.setGraphic(hbox); } else { cell.setGraphic(choiceBox); } } else { cell.setText(getItemText(cell, converter)); cell.setGraphic(graphic); } } } ; static <T> ChoiceBox<T> createChoiceBox( final Cell<T> cell, final ObservableList<T> items, final ObjectProperty<StringConverter<T>> converter) { ChoiceBox<T> choiceBox = new ChoiceBox<T>(items); choiceBox.setMaxWidth(Double.MAX_VALUE); choiceBox.converterProperty().bind(converter); choiceBox.getSelectionModel().selectedItemProperty().addListener((ov, oldValue, newValue) -> { if (cell.isEditing()) { cell.commitEdit(newValue); } }); return choiceBox; } /*************************************************************************** * * * TextField convenience * * * **************************************************************************/ static <T> void updateItem(final Cell<T> cell, final StringConverter<T> converter, final TextField textField) { updateItem(cell, converter, null, null, textField); } static <T> void updateItem(final Cell<T> cell, final StringConverter<T> converter, final HBox hbox, final Node graphic, final TextField textField) { if (cell.isEmpty()) { cell.setText(null); cell.setGraphic(null); } else { if (cell.isEditing()) { if (textField != null) { textField.setText(getItemText(cell, converter)); } cell.setText(null); if (graphic != null) { hbox.getChildren().setAll(graphic, textField); cell.setGraphic(hbox); } else { cell.setGraphic(textField); } } else { cell.setText(getItemText(cell, converter)); cell.setGraphic(graphic); } } } static <T> void startEdit(final Cell<T> cell, final StringConverter<T> converter, final HBox hbox, final Node graphic, final TextField textField) { if (textField != null) { textField.setText(getItemText(cell, converter)); } cell.setText(null); if (graphic != null) { hbox.getChildren().setAll(graphic, textField); cell.setGraphic(hbox); } else { cell.setGraphic(textField); } textField.selectAll(); // requesting focus so that key input can immediately go into the // TextField (see RT-28132) textField.requestFocus(); } static <T> void cancelEdit(Cell<T> cell, final StringConverter<T> converter, Node graphic) { cell.setText(getItemText(cell, converter)); cell.setGraphic(graphic); } static <T> TextField createTextField(final Cell<T> cell, final StringConverter<T> converter) { final TextField textField = new TextField(getItemText(cell, converter)); // Use onAction here rather than onKeyReleased (with check for Enter), // as otherwise we encounter RT-34685 textField.setOnAction(event -> { if (converter == null) { throw new IllegalStateException( "Attempting to convert text input into Object, but provided " + "StringConverter is null. Be sure to set a StringConverter " + "in your cell factory."); } cell.commitEdit(converter.fromString(textField.getText())); event.consume(); }); textField.setOnMouseExited(event -> { if (converter == null) { throw new IllegalStateException( "Attempting to convert text input into Object, but provided " + "StringConverter is null. Be sure to set a StringConverter " + "in your cell factory."); } cell.commitEdit(converter.fromString(textField.getText())); event.consume(); }); textField.setOnKeyReleased(t -> { if (t.getCode() == KeyCode.ESCAPE) { cell.cancelEdit(); t.consume(); } else if ((t.getCode() == KeyCode.UP) || (t.getCode() == KeyCode.DOWN) || (t.getCode() == KeyCode.LEFT) || (t.getCode() == KeyCode.RIGHT)) { if (converter == null) { throw new IllegalStateException( "Attempting to convert text input into Object, but provided " + "StringConverter is null. Be sure to set a StringConverter " + "in your cell factory."); } cell.commitEdit(converter.fromString(textField.getText())); t.consume(); } }); return textField; } /*************************************************************************** * * * ComboBox convenience * * * **************************************************************************/ static <T> void updateItem(Cell<T> cell, StringConverter<T> converter, ComboBox<T> comboBox) { updateItem(cell, converter, null, null, comboBox); } static <T> void updateItem(final Cell<T> cell, final StringConverter<T> converter, final HBox hbox, final Node graphic, final ComboBox<T> comboBox) { if (cell.isEmpty()) { cell.setText(null); cell.setGraphic(null); } else { if (cell.isEditing()) { if (comboBox != null) { comboBox.getSelectionModel().select(cell.getItem()); } cell.setText(null); if (graphic != null) { hbox.getChildren().setAll(graphic, comboBox); cell.setGraphic(hbox); } else { cell.setGraphic(comboBox); } } else { cell.setText(getItemText(cell, converter)); cell.setGraphic(graphic); } } } ; static <T> ComboBox<T> createComboBox(final Cell<T> cell, final ObservableList<T> items, final ObjectProperty<StringConverter<T>> converter) { ComboBox<T> comboBox = new ComboBox<T>(items); comboBox.converterProperty().bind(converter); comboBox.setMaxWidth(Double.MAX_VALUE); comboBox.getSelectionModel().selectedItemProperty().addListener((ov, oldValue, newValue) -> { if (cell.isEditing()) { cell.commitEdit(newValue); } }); return comboBox; } }