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.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
@ -50,6 +51,8 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.scene.control.TreeItem; import javafx.scene.control.TreeItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
/** /**
* Manages the configuration state of the emulator components. * Manages the configuration state of the emulator components.
@ -91,6 +94,11 @@ public class Configuration implements Reconfigurable {
return null; return null;
} }
public static ImageView getChangedIcon() {
InputStream imgStream = Configuration.class.getResourceAsStream("/jace/data/icon_exclaim.gif");
return new ImageView(new Image(imgStream));
}
@Override @Override
public String getName() { public String getName() {
return "Configuration"; return "Configuration";
@ -119,8 +127,8 @@ public class Configuration implements Reconfigurable {
public transient ConfigNode parent; public transient ConfigNode parent;
private ObservableList<ConfigNode> children; private ObservableList<ConfigNode> children;
public transient Reconfigurable subject; public transient Reconfigurable subject;
public Map<String, Serializable> settings; public Map<String, Serializable> settings = new TreeMap<>();
public Map<String, String[]> hotkeys; public Map<String, String[]> hotkeys = new TreeMap<>();;
private boolean changed = true; private boolean changed = true;
@Override @Override
@ -140,8 +148,6 @@ public class Configuration implements Reconfigurable {
public ConfigNode(ConfigNode parent, Reconfigurable subject) { public ConfigNode(ConfigNode parent, Reconfigurable subject) {
super(); super();
this.subject = subject; this.subject = subject;
this.settings = new TreeMap<>();
this.hotkeys = new TreeMap<>();
this.children = getChildren(); this.children = getChildren();
this.parent = parent; this.parent = parent;
if (this.parent != null) { if (this.parent != null) {
@ -151,7 +157,7 @@ public class Configuration implements Reconfigurable {
} }
public void setFieldValue(String field, Serializable value) { public void setFieldValue(String field, Serializable value) {
changed = true; setChanged(true);
if (value != null) { if (value != null) {
if (value.equals(getFieldValue(field))) { if (value.equals(getFieldValue(field))) {
return; return;
@ -208,6 +214,15 @@ public class Configuration implements Reconfigurable {
} }
children.add(index, newChild); children.add(index, newChild);
} }
private void setChanged(boolean b) {
changed = b;
if (!changed) {
setGraphic(null);
} else {
setGraphic(getChangedIcon());
}
}
} }
public static ConfigNode BASE; public static ConfigNode BASE;
public static EmulatorUILogic ui = Emulator.logic; 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); Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
} }
node.changed = false; node.setChanged(false);
} }
public static void applySettings(Map<String, String> settings) { 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.lang.reflect.Field;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map; import java.util.HashSet;
import java.util.Map.Entry;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.function.BiConsumer; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern;
import javafx.beans.Observable;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox; import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.control.SplitPane; import javafx.scene.control.SplitPane;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem; import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView; import javafx.scene.control.TreeView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.util.StringConverter; import javafx.util.StringConverter;
public class ConfigurationUIController { public class ConfigurationUIController {
public static final String DELIMITER = "~!~";
@FXML @FXML
private ResourceBundle resources; private ResourceBundle resources;
@ -53,23 +56,25 @@ public class ConfigurationUIController {
private ScrollPane treeScroll; private ScrollPane treeScroll;
@FXML @FXML
void reloadConfig(ActionEvent event) { void reloadConfig(MouseEvent event) {
Configuration.loadSettings();
resetDeviceTree();
} }
@FXML @FXML
void saveConfig(ActionEvent event) { void saveConfig(MouseEvent event) {
Configuration.saveSettings();
} }
@FXML @FXML
void applyConfig(ActionEvent event) { void applyConfig(MouseEvent event) {
Configuration.applySettings(Configuration.BASE);
resetDeviceTree();
} }
@FXML @FXML
void cancelConfig(ActionEvent event) { void cancelConfig(MouseEvent event) {
throw new RuntimeException("Not implemented yet");
} }
@FXML @FXML
@ -79,11 +84,67 @@ public class ConfigurationUIController {
assert settingsScroll != null : "fx:id=\"settingsScroll\" 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 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'."; 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.getSelectionModel().selectedItemProperty().addListener(this::selectionChanged);
deviceTree.maxWidthProperty().bind(treeScroll.widthProperty()); 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( private void selectionChanged(
ObservableValue<? extends TreeItem<ConfigNode>> observable, ObservableValue<? extends TreeItem<ConfigNode>> observable,
TreeItem<ConfigNode> oldValue, TreeItem<ConfigNode> oldValue,
@ -97,6 +158,9 @@ public class ConfigurationUIController {
} }
private void buildForm(ConfigNode node) { private void buildForm(ConfigNode node) {
if (node == null) {
return;
}
node.hotkeys.forEach((name, values) -> { node.hotkeys.forEach((name, values) -> {
settingsVbox.getChildren().add(buildKeyShortcutRow(node, 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) { 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) { 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) { 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) { private Node buildDynamicSelectComponent(ConfigNode node, String settingName, Serializable value) {
try { try {
DynamicSelection sel = (DynamicSelection) node.subject.getClass().getField(settingName).get(node.subject); 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() { widget.setConverter(new StringConverter() {
@Override @Override
public String toString(Object object) { public String toString(Object object) {
@ -191,9 +265,18 @@ public class ConfigurationUIController {
@Override @Override
public Object fromString(String string) { 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; return widget;
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
Logger.getLogger(ConfigurationUIController.class.getName()).log(Level.SEVERE, null, 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 @Override
public void setValue(T value) {currentValue = value;} public void setValue(T value) {currentValue = value;}
@Override
public void setValueByMatch(String search) { public void setValueByMatch(String search) {
setValue(findValueByMatch(search));
}
public T findValueByMatch(String search) {
Map<? extends T, String> selections = getSelections(); Map<? extends T, String> selections = getSelections();
String match = Utility.findBestMatch(search, selections.values()); String match = Utility.findBestMatch(search, selections.values());
if (match != null) { if (match != null) {
for (T key : selections.keySet()) { for (T key : selections.keySet()) {
if (selections.get(key).equals(match)) { if (selections.get(key).equals(match)) {
setValue(key); return key;
return;
} }
} }
} else {
setValue(null);
} }
return null;
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B