lawless-legends/OutlawEditor/src/main/java/org/badvision/outlaweditor/ui/ModalEditor.java

241 lines
8.8 KiB
Java

/*
* Copyright (C) 2015 The 8-Bit Bunch. Licensed under the Apache License, Version 1.1
* (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at <http://www.apache.org/licenses/LICENSE-1.1>.
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.badvision.outlaweditor.ui;
import static org.badvision.outlaweditor.data.DataUtilities.uppercaseFirst;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Control;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
/**
*
* @author blurry
*/
public class ModalEditor {
Dialog dialog;
public static interface EditControl<V> {
public Node getControl();
public V getValue();
public void setValue(V value);
}
public static class TextControl implements EditControl<String> {
TextField control = new TextField();
@Override
public Control getControl() {
return control;
}
@Override
public String getValue() {
return control.getText();
}
@Override
public void setValue(String value) {
control.setText(value);
}
}
static class TableControl<T, S> implements EditControl<List<T>> {
Map<String, Callback<TableColumn<T, S>, TableCell<T, S>>> cols;
Class<T> rowType;
ObservableList<T> rows;
TableView<T> table;
VBox control;
public TableControl(Map<String, Callback<TableColumn<T, S>, TableCell<T, S>>> cols, Class<T> rowType) {
this.cols = cols;
this.rowType = rowType;
rows = FXCollections.observableArrayList();
table = new TableView(rows);
cols.forEach((String colName, Callback<TableColumn<T, S>, TableCell<T, S>> factory) -> {
TableColumn<T, S> col = new TableColumn<>(uppercaseFirst(colName));
col.setCellValueFactory((TableColumn.CellDataFeatures<T, S> param) -> {
try {
return (ObservableValue<S>) JavaBeanStringPropertyBuilder.create().bean(param.getValue()).name(colName).build();
} catch (NoSuchMethodException ex) {
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, null, ex);
throw new RuntimeException(ex);
}
});
col.setCellFactory(factory);
col.setPrefWidth(150);
table.getColumns().add(col);
});
table.setPlaceholder(new Label("Click + to add one or more attributes"));
table.setEditable(true);
table.setPrefWidth(cols.size() * 150 + 5);
Button addButton = new Button("+");
addButton.setOnAction((event) -> {
try {
rows.add(rowType.newInstance());
Platform.runLater(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, null, ex);
}
table.edit(rows.size() - 1, table.getColumns().get(0));
});
} catch (InstantiationException | IllegalAccessException ex) {
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, null, ex);
}
});
Button removeButton = new Button("-");
removeButton.setOnAction((event) -> {
rows.removeAll(table.getSelectionModel().getSelectedItems());
});
addButton.setPrefWidth(25);
removeButton.setPrefWidth(25);
control = new VBox(
table,
new HBox(
10,
addButton,
removeButton
)
);
}
@Override
public Node getControl() {
return control;
}
@Override
public List<T> getValue() {
return rows;
}
@Override
public void setValue(List<T> value) {
rows.setAll(value);
}
}
public PropertyDescriptor getPropertyDescriptor(PropertyDescriptor[] descriptors, String propertyName) {
for (PropertyDescriptor descriptor : descriptors) {
if (descriptor.getName().equalsIgnoreCase(propertyName)) {
return descriptor;
}
}
return null;
}
public <T> Optional<T> editObject(T sourceObject, Map<String, EditControl> obj, Class<T> clazz, String title, String header) throws IntrospectionException {
BeanInfo info = Introspector.getBeanInfo(clazz);
dialog = new Dialog();
dialog.setTitle(title);
dialog.setHeaderText(header);
// dialog.setGraphic(...);
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 50, 10, 10));
final AtomicInteger row = new AtomicInteger(0);
obj.forEach((String property, EditControl control) -> {
PropertyDescriptor descriptor = getPropertyDescriptor(info.getPropertyDescriptors(), property);
if (row.get() == 0) {
Platform.runLater(() -> control.getControl().requestFocus());
}
grid.add(new Label(uppercaseFirst(descriptor.getDisplayName())), 0, row.get());
grid.add(control.getControl(), 1, row.getAndAdd(1));
try {
control.setValue(descriptor.getReadMethod().invoke(sourceObject));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, null, ex);
}
});
dialog.getDialogPane().setContent(grid);
dialog.setResultConverter(dialogButton -> {
if (dialogButton == ButtonType.OK) {
obj.forEach((String property, EditControl control) -> {
PropertyDescriptor descriptor = getPropertyDescriptor(info.getPropertyDescriptors(), property);
try {
if (descriptor.getWriteMethod() != null) {
descriptor.getWriteMethod().invoke(sourceObject, control.getValue());
} else {
Object val = descriptor.getReadMethod().invoke(sourceObject);
if (val instanceof List sourceList) {
sourceList.clear();
sourceList.addAll((Collection) control.getValue());
}
}
} catch (NullPointerException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, "Error updating property " + property, ex);
}
});
return sourceObject;
}
return null;
});
return dialog.showAndWait();
}
public boolean isOpen() {
return dialog != null && dialog.isShowing();
}
public void close() {
if (isOpen()) {
dialog.close();
}
}
}