Config screen hooked up to load/save/apply actions, tree state is now properly reflected and maintained. Also some settings editor widgets are working.

This commit is contained in:
Brendan Robert 2015-03-11 02:21:35 -05:00
parent 9a9649c961
commit 8959c412ed
4 changed files with 128 additions and 27 deletions

View File

@ -28,6 +28,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@ -50,6 +51,8 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.collections.ObservableList;
import javafx.scene.control.TreeItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
/**
* Manages the configuration state of the emulator components.
@ -91,6 +94,11 @@ public class Configuration implements Reconfigurable {
return null;
}
public static ImageView getChangedIcon() {
InputStream imgStream = Configuration.class.getResourceAsStream("/jace/data/icon_exclaim.gif");
return new ImageView(new Image(imgStream));
}
@Override
public String getName() {
return "Configuration";
@ -119,8 +127,8 @@ public class Configuration implements Reconfigurable {
public transient ConfigNode parent;
private ObservableList<ConfigNode> children;
public transient Reconfigurable subject;
public Map<String, Serializable> settings;
public Map<String, String[]> hotkeys;
public Map<String, Serializable> settings = new TreeMap<>();
public Map<String, String[]> hotkeys = new TreeMap<>();;
private boolean changed = true;
@Override
@ -140,8 +148,6 @@ public class Configuration implements Reconfigurable {
public ConfigNode(ConfigNode parent, Reconfigurable subject) {
super();
this.subject = subject;
this.settings = new TreeMap<>();
this.hotkeys = new TreeMap<>();
this.children = getChildren();
this.parent = parent;
if (this.parent != null) {
@ -151,7 +157,7 @@ public class Configuration implements Reconfigurable {
}
public void setFieldValue(String field, Serializable value) {
changed = true;
setChanged(true);
if (value != null) {
if (value.equals(getFieldValue(field))) {
return;
@ -208,6 +214,15 @@ public class Configuration implements Reconfigurable {
}
children.add(index, newChild);
}
private void setChanged(boolean b) {
changed = b;
if (!changed) {
setGraphic(null);
} else {
setGraphic(getChangedIcon());
}
}
}
public static ConfigNode BASE;
public static EmulatorUILogic ui = Emulator.logic;
@ -512,7 +527,7 @@ public class Configuration implements Reconfigurable {
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
}
node.changed = false;
node.setChanged(false);
}
public static void applySettings(Map<String, String> settings) {

View File

@ -6,30 +6,33 @@ import java.io.Serializable;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.HashSet;
import java.util.ResourceBundle;
import java.util.function.BiConsumer;
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.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
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;
@ -53,23 +56,25 @@ public class ConfigurationUIController {
private ScrollPane treeScroll;
@FXML
void reloadConfig(ActionEvent event) {
void reloadConfig(MouseEvent event) {
Configuration.loadSettings();
resetDeviceTree();
}
@FXML
void saveConfig(ActionEvent event) {
void saveConfig(MouseEvent event) {
Configuration.saveSettings();
}
@FXML
void applyConfig(ActionEvent event) {
void applyConfig(MouseEvent event) {
Configuration.applySettings(Configuration.BASE);
resetDeviceTree();
}
@FXML
void cancelConfig(ActionEvent event) {
void cancelConfig(MouseEvent event) {
throw new RuntimeException("Not implemented yet");
}
@FXML
@ -79,11 +84,67 @@ public class ConfigurationUIController {
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'.";
deviceTree.setRoot(Configuration.BASE);
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;
for (TreeItem item : root.getChildren()) {
if (item.isExpanded()) {
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,
@ -97,6 +158,9 @@ public class ConfigurationUIController {
}
private void buildForm(ConfigNode node) {
if (node == null) {
return;
}
node.hotkeys.forEach((name, values) -> {
settingsVbox.getChildren().add(buildKeyShortcutRow(node, name, values));
});
@ -168,11 +232,20 @@ public class ConfigurationUIController {
}
private Node buildTextField(ConfigNode node, String settingName, Serializable value, String validationPattern) {
return new TextField(String.valueOf(value));
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) {
return new CheckBox();
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) {
@ -182,7 +255,8 @@ public class ConfigurationUIController {
private Node buildDynamicSelectComponent(ConfigNode node, String settingName, Serializable value) {
try {
DynamicSelection sel = (DynamicSelection) node.subject.getClass().getField(settingName).get(node.subject);
ComboBox widget = new ComboBox(FXCollections.observableList(new ArrayList(sel.getSelections().keySet())));
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) {
@ -191,9 +265,18 @@ public class ConfigurationUIController {
@Override
public Object fromString(String string) {
return null;
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);

View File

@ -45,18 +45,21 @@ public abstract class DynamicSelection<T> implements ISelection<T> {
@Override
public void setValue(T value) {currentValue = value;}
@Override
public void setValueByMatch(String search) {
setValue(findValueByMatch(search));
}
public T findValueByMatch(String search) {
Map<? extends T, String> selections = getSelections();
String match = Utility.findBestMatch(search, selections.values());
if (match != null) {
for (T key : selections.keySet()) {
if (selections.get(key).equals(match)) {
setValue(key);
return;
return key;
}
}
} else {
setValue(null);
}
return null;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B