Configuration tree is now completely visible in new JavaFX config dialog but things are showing as duplicated at the root level

This commit is contained in:
Brendan Robert 2015-02-24 00:30:11 -06:00
parent 7e5ddac0e6
commit a6a1bf681b
9 changed files with 255 additions and 153 deletions

View File

@ -21,6 +21,7 @@ package jace;
import jace.apple2e.MOS65C02;
import jace.apple2e.RAM128k;
import jace.apple2e.SoftSwitches;
import jace.config.ConfigurationUIController;
import jace.config.InvokableAction;
import jace.config.Reconfigurable;
import jace.core.CPU;
@ -43,7 +44,11 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
@ -355,11 +360,19 @@ public class EmulatorUILogic implements Reconfigurable {
alternatives = "Reconfigure,Preferences,Settings",
defaultKeyMapping = {"f4","ctrl+shift+c"})
public static void showConfig() {
// if (Emulator.getFrame().getModalDialogUI(CONFIGURATION_DIALOG_NAME) == null) {
// JPanel ui = new ConfigurationPanel();
// Emulator.getFrame().registerModalDialog(ui, CONFIGURATION_DIALOG_NAME, null, false);
// }
// Emulator.getFrame().showDialog(CONFIGURATION_DIALOG_NAME);
FXMLLoader fxmlLoader = new FXMLLoader(EmulatorUILogic.class.getResource("/fxml/Configuration.fxml"));
fxmlLoader.setResources(null);
try {
Stage configWindow = new Stage();
AnchorPane node = (AnchorPane) fxmlLoader.load();
ConfigurationUIController controller = fxmlLoader.getController();
controller.initialize();
Scene s = new Scene(node);
configWindow.setScene(s);
configWindow.show();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public static final String MEDIA_MANAGER_DIALOG_NAME = "Media Manager";

View File

@ -19,12 +19,13 @@ import javafx.stage.Stage;
* @author blurry
*/
public class JaceApplication extends Application {
static JaceApplication singleton;
Stage primaryStage;
JaceUIController controller;
@Override
public void start(Stage stage) throws Exception {
singleton = this;
primaryStage = stage;
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/JaceUI.fxml"));
fxmlLoader.setResources(null);
@ -53,6 +54,10 @@ public class JaceApplication extends Application {
});
}
public static JaceApplication getApplication() {
return singleton;
}
/**
* @param args the command line arguments
*/

View File

@ -7,9 +7,7 @@
package jace;
import jace.core.Computer;
import jace.core.Video;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.image.ImageView;
@ -49,7 +47,6 @@ public class JaceUIController {
rootPane.setFocusTraversable(true);
rootPane.setOnKeyPressed(keyboardHandler);
rootPane.setOnKeyReleased(keyboardHandler);
// rootPane.onKeyTypedProperty().setValue(keyboardHandler);
rootPane.requestFocus();
}
}

View File

@ -48,9 +48,8 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javafx.collections.ObservableList;
import javafx.scene.control.TreeItem;
/**
* Manages the configuration state of the emulator components.
@ -82,68 +81,6 @@ public class Configuration implements Reconfigurable {
public void reconfigure() {
}
public static class ConfigTreeModel implements TreeModel {
@Override
public Object getRoot() {
return BASE;
}
@Override
public Object getChild(Object parent, int index) {
if (parent instanceof ConfigNode) {
ConfigNode n = (ConfigNode) parent;
return n.children.values().toArray()[index];
} else {
return null;
}
}
@Override
public int getChildCount(Object parent) {
if (parent instanceof ConfigNode) {
ConfigNode n = (ConfigNode) parent;
return n.children.size();
} else {
return 0;
}
}
@Override
public boolean isLeaf(Object node) {
return getChildCount(node) == 0;
}
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
// Do nothing...
}
@Override
public int getIndexOfChild(Object parent, Object child) {
if (parent instanceof ConfigNode) {
ConfigNode n = (ConfigNode) parent;
ConfigNode[] c = (ConfigNode[]) n.children.values().toArray(new ConfigNode[0]);
for (int i = 0; i < c.length; i++) {
if (c[i].equals(child)) {
return i;
}
}
}
return -1;
}
@Override
public void addTreeModelListener(TreeModelListener l) {
// Do nothing...
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
// Do nothing...
}
}
/**
* Represents a serializable configuration node as part of a tree. The root
* node should be a single instance (e.g. Computer) The child nodes should
@ -152,14 +89,14 @@ public class Configuration implements Reconfigurable {
* configuration 2) Provide a simple persistence mechanism to load/store
* configuration
*/
public static class ConfigNode implements Serializable {
public static class ConfigNode extends TreeItem implements Serializable {
public transient ConfigNode root;
public transient ConfigNode parent;
private ObservableList<ConfigNode> children;
public transient Reconfigurable subject;
private Map<String, Serializable> settings;
private Map<String, String[]> hotkeys;
protected Map<String, ConfigNode> children;
private boolean changed = true;
@Override
@ -167,23 +104,26 @@ public class Configuration implements Reconfigurable {
if (subject == null) {
return "???";
}
return (changed ? "<html><i>" : "") + subject.getName();
return subject.getName();
}
public ConfigNode(Reconfigurable subject) {
this(null, subject);
this.root = null;
this.setExpanded(true);
}
public ConfigNode(ConfigNode parent, Reconfigurable subject) {
super();
this.subject = subject;
this.settings = new TreeMap<>();
this.hotkeys = new TreeMap<>();
this.children = new TreeMap<>();
this.children = getChildren();
this.parent = parent;
if (this.parent != null) {
this.root = this.parent.root != null ? this.parent.root : this.parent;
}
setValue(toString());
}
public void setFieldValue(String field, Serializable value) {
@ -211,6 +151,38 @@ public class Configuration implements Reconfigurable {
public Set<String> getAllSettingNames() {
return settings.keySet();
}
@Override
public ObservableList<ConfigNode> getChildren() {
return super.getChildren();
}
private boolean removeChild(String childName) {
ConfigNode child = findChild(childName);
return children.remove(child);
}
private ConfigNode findChild(String childName) {
for (ConfigNode node : children) {
if (childName.equalsIgnoreCase(String.valueOf(node.getValue()))) {
return node;
}
}
return null;
}
private void putChild(String childName, ConfigNode newChild) {
removeChild(childName);
int index = 0;
for (ConfigNode node : children) {
if (String.valueOf(node.getValue()).compareToIgnoreCase(childName) > 0) {
break;
} else {
index++;
}
}
children.add(index, newChild);
}
}
public final static ConfigNode BASE;
public static EmulatorUILogic ui = Emulator.logic;
@ -241,7 +213,7 @@ public class Configuration implements Reconfigurable {
}
for (Field f : node.subject.getClass().getFields()) {
// System.out.println("Evaluating field " + f.getName());
System.out.println("Evaluating field " + f.getName());
try {
Object o = f.get(node.subject);
if (/*o == null ||*/visited.contains(o)) {
@ -267,22 +239,22 @@ public class Configuration implements Reconfigurable {
if (o instanceof Reconfigurable) {
Reconfigurable r = (Reconfigurable) o;
ConfigNode child = node.children.get(f.getName());
ConfigNode child = node.findChild(f.getName());
if (child == null || !child.subject.equals(o)) {
child = new ConfigNode(node, r);
node.children.put(f.getName(), child);
node.putChild(f.getName(), child);
}
buildTree(child, visited);
} else if (o.getClass().isArray()) {
String fieldName = f.getName();
Class type = o.getClass().getComponentType();
// System.out.println("Evaluating " + node.subject.getShortName() + "." + fieldName + "; type is " + type.toGenericString());
System.out.println("Evaluating " + node.subject.getShortName() + "." + fieldName + "; type is " + type.toGenericString());
List<Reconfigurable> children = new ArrayList<>();
if (!Reconfigurable.class.isAssignableFrom(type)) {
// System.out.println("Looking at type " + type.getName() + " to see if optional");
System.out.println("Looking at type " + type.getName() + " to see if optional");
if (Optional.class.isAssignableFrom(type)) {
Type genericTypes = f.getGenericType();
// System.out.println("Looking at generic parmeters " + genericTypes.getTypeName() + " for reconfigurable class, type "+genericTypes.getClass().getName());
System.out.println("Looking at generic parmeters " + genericTypes.getTypeName() + " for reconfigurable class, type "+genericTypes.getClass().getName());
if (genericTypes instanceof GenericArrayType) {
GenericArrayType aType = (GenericArrayType) genericTypes;
ParameterizedType pType = (ParameterizedType) aType.getGenericComponentType();
@ -313,13 +285,13 @@ public class Configuration implements Reconfigurable {
Reconfigurable child = children.get(i);
String childName = fieldName + i;
if (child == null) {
node.children.remove(childName);
node.removeChild(childName);
continue;
}
ConfigNode grandchild = node.children.get(childName);
ConfigNode grandchild = node.findChild(childName);
if (grandchild == null || !grandchild.subject.equals(child)) {
grandchild = new ConfigNode(node, child);
node.children.put(childName, grandchild);
node.putChild(childName, grandchild);
}
buildTree(grandchild, visited);
}
@ -426,7 +398,7 @@ public class Configuration implements Reconfigurable {
// Now that the object structure reflects the current configuration,
// process reconfiguration from the children, etc.
for (ConfigNode child : node.children.values()) {
for (ConfigNode child : node.getChildren()) {
hasChanged |= applySettings(child);
}
@ -451,9 +423,10 @@ public class Configuration implements Reconfigurable {
doApply(oldRoot);
buildTree(oldRoot, new HashSet());
}
newRoot.children.keySet().stream().forEach((childName) -> {
newRoot.getChildren().stream().forEach((child) -> {
String childName = child.getValue().toString();
// System.out.println("Applying settings for " + childName);
applyConfigTree(newRoot.children.get(childName), oldRoot.children.get(childName));
applyConfigTree(newRoot.findChild(childName), oldRoot.findChild(childName));
});
}
@ -568,8 +541,8 @@ public class Configuration implements Reconfigurable {
private static void buildNodeMap(ConfigNode n, Map<String, ConfigNode> shortNames) {
// System.out.println("Encountered " + n.subject.getShortName().toLowerCase());
shortNames.put(n.subject.getShortName().toLowerCase(), n);
n.children.entrySet().stream().forEach((c) -> {
buildNodeMap(c.getValue(), shortNames);
n.getChildren().stream().forEach((c) -> {
buildNodeMap(c, shortNames);
});
}
@ -587,8 +560,8 @@ public class Configuration implements Reconfigurable {
String sn = (f != null && !f.shortName().equals("")) ? f.shortName() : setting;
System.out.println(prefix + ">>" + setting + " (" + n.subject.getShortName() + "." + sn + ")");
});
n.children.entrySet().stream().forEach((c) -> {
printTree(c.getValue(), prefix + "." + c.getKey(), i + 1);
n.getChildren().stream().forEach((c) -> {
printTree(c, prefix + "." + c.toString(), i + 1);
});
}
}

View File

@ -62,12 +62,15 @@
<Component class="javax.swing.JTree" name="configTree">
<Properties>
<Property name="model" type="javax.swing.tree.TreeModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Configuration.ConfigTreeModel()" type="code"/>
<Connection code="null); //new Configuration.ConfigTreeModel()" type="code"/>
</Property>
</Properties>
<Events>
<EventHandler event="valueChanged" listener="javax.swing.event.TreeSelectionListener" parameters="javax.swing.event.TreeSelectionEvent" handler="configTreeValueChanged"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_SerializeTo" type="java.lang.String" value="ConfigurationPanel_configTree"/>
</AuxValues>
</Component>
</SubComponents>
</Container>

View File

@ -77,72 +77,72 @@ public class ConfigurationPanel extends javax.swing.JPanel {
setPreferredSize(new java.awt.Dimension(650, 480));
configTree.setModel(new Configuration.ConfigTreeModel());
configTree.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
configTreeValueChanged(evt);
}
});
configTreeScrollPane.setViewportView(configTree);
configTree.setModel(null); //new Configuration.ConfigTreeModel());
configTree.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
configTreeValueChanged(evt);
}
});
configTreeScrollPane.setViewportView(configTree);
settingsPanel.setAlignmentX(0.0F);
settingsPanel.setAlignmentY(0.0F);
settingsPanel.setPreferredSize(new java.awt.Dimension(375, 480));
settingsPanel.setLayout(new java.awt.GridBagLayout());
settingsPanel.setAlignmentX(0.0F);
settingsPanel.setAlignmentY(0.0F);
settingsPanel.setPreferredSize(new java.awt.Dimension(375, 480));
settingsPanel.setLayout(new java.awt.GridBagLayout());
applyButton.setText("Apply");
applyButton.setToolTipText("Apply current changes without saving");
applyButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
applyButtonActionPerformed(evt);
}
});
applyButton.setText("Apply");
applyButton.setToolTipText("Apply current changes without saving");
applyButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
applyButtonActionPerformed(evt);
}
});
saveButton.setText("Save");
saveButton.setToolTipText("Apply settings and save");
saveButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
saveButtonActionPerformed(evt);
}
});
saveButton.setText("Save");
saveButton.setToolTipText("Apply settings and save");
saveButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
saveButtonActionPerformed(evt);
}
});
revertButton.setText("Revert");
revertButton.setToolTipText("Revert all settings to last saved values (hold SHIFT while clicking to revert to defaults)");
revertButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
revertButtonActionPerformed(evt);
}
});
revertButton.setText("Revert");
revertButton.setToolTipText("Revert all settings to last saved values (hold SHIFT while clicking to revert to defaults)");
revertButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
revertButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(applyButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(saveButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(revertButton))
.addComponent(configTreeScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 271, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(settingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 438, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(configTreeScrollPane)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(applyButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(saveButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(revertButton))
.addContainerGap())
.addComponent(settingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 0, Short.MAX_VALUE)
);
.addComponent(configTreeScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 271, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(settingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 438, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(configTreeScrollPane)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(applyButton)
.addComponent(saveButton)
.addComponent(revertButton))
.addContainerGap())
.addComponent(settingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 0, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
ConfigNode currentNode = null;

View File

@ -0,0 +1,66 @@
package jace.config;
import jace.Emulator;
import jace.JaceApplication;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TreeView;
import javafx.scene.layout.VBox;
public class ConfigurationUIController {
@FXML
private ResourceBundle resources;
@FXML
private URL location;
@FXML
private VBox settingsVbox;
@FXML
private SplitPane splitPane;
@FXML
private ScrollPane settingsScroll;
@FXML
private TreeView<?> deviceTree;
@FXML
private ScrollPane treeScroll;
@FXML
void reloadConfig(ActionEvent event) {
}
@FXML
void saveConfig(ActionEvent event) {
}
@FXML
void applyConfig(ActionEvent event) {
}
@FXML
void cancelConfig(ActionEvent event) {
}
@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'.";
deviceTree.setRoot(Configuration.BASE);
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" styleClass="mainFxmlClass" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="jace.config.ConfigurationUIController">
<stylesheets>
<URL value="@/styles/configuration.css" />
</stylesheets>
<children>
<ToolBar prefHeight="40.0" prefWidth="600.0">
<items>
<Button mnemonicParsing="false" onMouseClicked="#reloadConfig" text="Reload" />
<Button mnemonicParsing="false" onMouseClicked="#saveConfig" text="Save" />
<Button mnemonicParsing="false" onMouseClicked="#applyConfig" text="Apply" />
<Button mnemonicParsing="false" onMouseClicked="#cancelConfig" text="Cancel" />
</items>
</ToolBar>
<SplitPane fx:id="splitPane" dividerPositions="0.3979933110367893" layoutY="40.0" prefHeight="363.0" prefWidth="600.0">
<items>
<ScrollPane fx:id="treeScroll" prefHeight="361.0" prefWidth="174.0">
<content>
<TreeView fx:id="deviceTree" prefHeight="359.0" prefWidth="233.0" />
</content>
</ScrollPane>
<ScrollPane fx:id="settingsScroll" prefHeight="361.0" prefWidth="416.0">
<content>
<VBox fx:id="settingsVbox" prefHeight="360.0" prefWidth="354.0" />
</content>
</ScrollPane>
</items>
</SplitPane>
</children>
</AnchorPane>

View File

@ -0,0 +1,7 @@
/*
* Empty Stylesheet file.
*/
.mainFxmlClass {
}