mirror of
https://github.com/badvision/jace.git
synced 2024-06-10 07:29:30 +00:00
285 lines
10 KiB
Java
285 lines
10 KiB
Java
package jace.config;
|
|
|
|
import jace.config.Configuration.ConfigNode;
|
|
import java.io.File;
|
|
import java.io.Serializable;
|
|
import java.lang.reflect.Field;
|
|
import java.net.URL;
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.ResourceBundle;
|
|
import java.util.Set;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import java.util.regex.Pattern;
|
|
import javafx.beans.Observable;
|
|
import javafx.beans.value.ObservableValue;
|
|
import javafx.collections.FXCollections;
|
|
import javafx.fxml.FXML;
|
|
import javafx.scene.Node;
|
|
import javafx.scene.control.CheckBox;
|
|
import javafx.scene.control.ChoiceBox;
|
|
import javafx.scene.control.Label;
|
|
import javafx.scene.control.ScrollPane;
|
|
import javafx.scene.control.SplitPane;
|
|
import javafx.scene.control.TextField;
|
|
import javafx.scene.control.TreeItem;
|
|
import javafx.scene.control.TreeView;
|
|
import javafx.scene.input.MouseEvent;
|
|
import javafx.scene.layout.HBox;
|
|
import javafx.scene.layout.VBox;
|
|
import javafx.util.StringConverter;
|
|
|
|
public class ConfigurationUIController {
|
|
public static final String DELIMITER = "~!~";
|
|
|
|
@FXML
|
|
private ResourceBundle resources;
|
|
|
|
@FXML
|
|
private URL location;
|
|
|
|
@FXML
|
|
private VBox settingsVbox;
|
|
|
|
@FXML
|
|
private SplitPane splitPane;
|
|
|
|
@FXML
|
|
private ScrollPane settingsScroll;
|
|
|
|
@FXML
|
|
private TreeView<ConfigNode> deviceTree;
|
|
|
|
@FXML
|
|
private ScrollPane treeScroll;
|
|
|
|
@FXML
|
|
void reloadConfig(MouseEvent event) {
|
|
Configuration.loadSettings();
|
|
resetDeviceTree();
|
|
}
|
|
|
|
@FXML
|
|
void saveConfig(MouseEvent event) {
|
|
Configuration.saveSettings();
|
|
}
|
|
|
|
@FXML
|
|
void applyConfig(MouseEvent event) {
|
|
Configuration.applySettings(Configuration.BASE);
|
|
resetDeviceTree();
|
|
}
|
|
|
|
@FXML
|
|
void cancelConfig(MouseEvent event) {
|
|
Configuration.buildTree();
|
|
resetDeviceTree();
|
|
}
|
|
|
|
@FXML
|
|
public void initialize() {
|
|
assert settingsVbox != null : "fx:id=\"settingsVbox\" was not injected: check your FXML file 'Configuration.fxml'.";
|
|
assert splitPane != null : "fx:id=\"splitPane\" was not injected: check your FXML file 'Configuration.fxml'.";
|
|
assert settingsScroll != null : "fx:id=\"settingsScroll\" was not injected: check your FXML file 'Configuration.fxml'.";
|
|
assert deviceTree != null : "fx:id=\"deviceTree\" was not injected: check your FXML file 'Configuration.fxml'.";
|
|
assert treeScroll != null : "fx:id=\"treeScroll\" was not injected: check your FXML file 'Configuration.fxml'.";
|
|
resetDeviceTree();
|
|
deviceTree.getSelectionModel().selectedItemProperty().addListener(this::selectionChanged);
|
|
deviceTree.maxWidthProperty().bind(treeScroll.widthProperty());
|
|
}
|
|
|
|
private void resetDeviceTree() {
|
|
Set<String> expanded = new HashSet<>();
|
|
String current = getCurrentNodePath();
|
|
getExpandedNodes("", deviceTree.getRoot(), expanded);
|
|
deviceTree.setRoot(Configuration.BASE);
|
|
setExpandedNodes("", deviceTree.getRoot(), expanded);
|
|
setCurrentNodePath(current);
|
|
}
|
|
|
|
private void getExpandedNodes(String prefix, TreeItem<ConfigNode> root, Set<String> expanded) {
|
|
if (root == null) return;
|
|
root.getChildren().stream().filter((item) -> (item.isExpanded())).forEach((item) -> {
|
|
String name = prefix+item.toString();
|
|
expanded.add(name);
|
|
getExpandedNodes(name+DELIMITER, item, expanded);
|
|
});
|
|
}
|
|
|
|
private void setExpandedNodes(String prefix, TreeItem<ConfigNode> root, Set<String> expanded) {
|
|
if (root == null) return;
|
|
root.getChildren().stream().forEach((item) -> {
|
|
String name = prefix+item.toString();
|
|
if (expanded.contains(name)) {
|
|
item.setExpanded(true);
|
|
}
|
|
setExpandedNodes(name+DELIMITER, item, expanded);
|
|
});
|
|
}
|
|
|
|
private String getCurrentNodePath() {
|
|
TreeItem<ConfigNode> current = deviceTree.getSelectionModel().getSelectedItem();
|
|
if (current == null) return null;
|
|
String out = current.toString();
|
|
while (current.getParent() != null) {
|
|
out = current.getParent().toString()+DELIMITER+current;
|
|
current = current.getParent();
|
|
}
|
|
return out;
|
|
}
|
|
|
|
private void setCurrentNodePath(String value) {
|
|
if (value == null) return;
|
|
String[] parts = value.split(Pattern.quote(DELIMITER));
|
|
TreeItem<ConfigNode> current = deviceTree.getRoot();
|
|
for (String part : parts) {
|
|
for (TreeItem child : current.getChildren()) {
|
|
if (child.toString().equals(part)) {
|
|
current = child;
|
|
}
|
|
}
|
|
}
|
|
deviceTree.getSelectionModel().select(current);
|
|
}
|
|
|
|
private void selectionChanged(
|
|
ObservableValue<? extends TreeItem<ConfigNode>> observable,
|
|
TreeItem<ConfigNode> oldValue,
|
|
TreeItem<ConfigNode> newValue) {
|
|
clearForm();
|
|
buildForm((ConfigNode) newValue);
|
|
}
|
|
|
|
private void clearForm() {
|
|
settingsVbox.getChildren().clear();
|
|
}
|
|
|
|
private void buildForm(ConfigNode node) {
|
|
if (node == null) {
|
|
return;
|
|
}
|
|
node.hotkeys.forEach((name, values) -> {
|
|
settingsVbox.getChildren().add(buildKeyShortcutRow(node, name, values));
|
|
});
|
|
node.settings.forEach((name, value) -> {
|
|
settingsVbox.getChildren().add(buildSettingRow(node, name, value));
|
|
});
|
|
}
|
|
|
|
private Node buildSettingRow(ConfigNode node, String settingName, Serializable value) {
|
|
ConfigurableField fieldInfo = Configuration.getConfigurableFieldInfo(node.subject, settingName);
|
|
if (fieldInfo == null) {
|
|
return null;
|
|
}
|
|
HBox row = new HBox();
|
|
Label label = new Label(fieldInfo.name());
|
|
label.getStyleClass().add("setting-label");
|
|
label.setMinWidth(150.0);
|
|
Node widget = buildEditField(node, settingName, value);
|
|
label.setLabelFor(widget);
|
|
row.getChildren().add(label);
|
|
row.getChildren().add(widget);
|
|
return row;
|
|
}
|
|
|
|
private Node buildKeyShortcutRow(ConfigNode node, String actionName, String[] values) {
|
|
InvokableAction actionInfo = Configuration.getInvokableActionInfo(node.subject, actionName);
|
|
if (actionInfo == null) {
|
|
return null;
|
|
}
|
|
HBox row = new HBox();
|
|
Label label = new Label(actionInfo.name());
|
|
label.getStyleClass().add("setting-keyboard-shortcut");
|
|
label.setMinWidth(150.0);
|
|
TextField widget = new TextField(String.valueOf(values));
|
|
label.setLabelFor(widget);
|
|
row.getChildren().add(label);
|
|
row.getChildren().add(widget);
|
|
return row;
|
|
}
|
|
|
|
private Node buildEditField(ConfigNode node, String settingName, Serializable value) {
|
|
Field field;
|
|
try {
|
|
field = node.subject.getClass().getField(settingName);
|
|
} catch (NoSuchFieldException | SecurityException ex) {
|
|
return null;
|
|
}
|
|
Class type = field.getType();
|
|
if (type == java.lang.String.class) {
|
|
return buildTextField(node, settingName, value, null);
|
|
} else if (type.isPrimitive()) {
|
|
if (type == Integer.TYPE || type == Short.TYPE || type == Byte.TYPE) {
|
|
return buildTextField(node, settingName, value, "-?[0-9]+");
|
|
} else if (type == Float.TYPE || type == Double.TYPE) {
|
|
return buildTextField(node, settingName, value, "-?[0-9]*(\\.[0-9]+)?");
|
|
} else if (type == Boolean.TYPE) {
|
|
return buildBooleanField(node, settingName, value);
|
|
} else {
|
|
return buildTextField(node, settingName, value, null);
|
|
}
|
|
} else if (type.equals(File.class)) {
|
|
return buildFileSelectionField(node, settingName, value);
|
|
} else if (Class.class.isEnum()) {
|
|
// TODO: Add enumeration support!
|
|
} else if (ISelection.class.isAssignableFrom(type)) {
|
|
return buildDynamicSelectComponent(node, settingName, value);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private Node buildTextField(ConfigNode node, String settingName, Serializable value, String validationPattern) {
|
|
TextField widget = new TextField(String.valueOf(value));
|
|
widget.textProperty().addListener((e) -> {
|
|
node.setFieldValue(settingName, widget.getText());
|
|
});
|
|
return widget;
|
|
}
|
|
|
|
private Node buildBooleanField(ConfigNode node, String settingName, Serializable value) {
|
|
CheckBox widget = new CheckBox();
|
|
widget.setSelected(value.equals(Boolean.TRUE));
|
|
widget.selectedProperty().addListener((e) -> {
|
|
node.setFieldValue(settingName, widget.isSelected());
|
|
});
|
|
return widget;
|
|
}
|
|
|
|
private Node buildFileSelectionField(ConfigNode node, String settingName, Serializable value) {
|
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
|
}
|
|
|
|
private Node buildDynamicSelectComponent(ConfigNode node, String settingName, Serializable value) {
|
|
try {
|
|
DynamicSelection sel = (DynamicSelection) node.subject.getClass().getField(settingName).get(node.subject);
|
|
ChoiceBox widget = new ChoiceBox(FXCollections.observableList(new ArrayList(sel.getSelections().keySet())));
|
|
widget.setMinWidth(175.0);
|
|
widget.setConverter(new StringConverter() {
|
|
@Override
|
|
public String toString(Object object) {
|
|
return (String) sel.getSelections().get(object);
|
|
}
|
|
|
|
@Override
|
|
public Object fromString(String string) {
|
|
return sel.findValueByMatch(string);
|
|
}
|
|
});
|
|
Object selected = value == null ? null : widget.getConverter().fromString(String.valueOf(value));
|
|
if (selected == null) {
|
|
widget.getSelectionModel().selectFirst();
|
|
} else {
|
|
widget.setValue(selected);
|
|
}
|
|
widget.valueProperty().addListener((Observable e) -> {
|
|
node.setFieldValue(settingName, widget.getConverter().toString(widget.getValue()));
|
|
});
|
|
return widget;
|
|
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
|
|
Logger.getLogger(ConfigurationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
|
return null;
|
|
}
|
|
}
|
|
}
|