Added ability to edit and prepare nicer views for shards.

This commit is contained in:
Andrew Lalis 2021-05-29 23:09:39 +02:00
parent f7df8a1eaf
commit 5ab1abbfff
20 changed files with 396 additions and 60 deletions

View File

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333333 4.2333333"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="login_credentials_shard_node_icon.svg"
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\nl\andrewlalis\crystalkeep\ui\images\login_credentials_shard_node_icon.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="89.600001"
inkscape:cx="10.229603"
inkscape:cy="7.4126105"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
units="px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76667)">
<path
sodipodi:type="star"
style="fill:#72deff;fill-opacity:1;stroke:none;stroke-width:0.07803907;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
id="path817"
sodipodi:sides="9"
sodipodi:cx="2.1166666"
sodipodi:cy="294.93815"
sodipodi:r1="1.8178079"
sodipodi:r2="1.2179312"
sodipodi:arg1="0.52359878"
sodipodi:arg2="0.87266463"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 3.6909344,295.84704 -0.7913968,0.0241 -0.1611441,0.77519 -0.6217269,-0.49025 -0.621727,0.49025 -0.1611441,-0.77519 -0.79139678,-0.0241 0.37483979,-0.69741 -0.59076328,-0.52715 0.73543197,-0.2933 -0.11370504,-0.78356 0.75190744,0.24804 0.416557,-0.67333 0.4165569,0.67333 0.7519075,-0.24804 -0.1137051,0.78356 0.735432,0.2933 -0.5907633,0.52715 z"
inkscape:transform-center-x="0.06028767"
inkscape:transform-center-y="0.030212626" />
<g
id="g828">
<rect
y="294.87439"
x="1.5420023"
height="0.95215333"
width="1.1493286"
id="rect816"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.05291667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
<path
inkscape:connector-curvature="0"
id="path818"
d="m 1.8074821,294.89943 v -0.43431 c 0,0 -0.016704,-0.32365 0.3236467,-0.32365 0.340351,0 0.292326,0.29233 0.292326,0.29233 v 0.47816"
style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
y="295.05811"
x="1.7157624"
height="0.17957026"
width="0.8018086"
id="rect820"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.05291667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.05291667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
id="rect822"
width="0.8018086"
height="0.17957026"
x="1.7157624"
y="295.41309" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333333 4.2333333"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="text_shard_node_icon.svg"
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\nl\andrewlalis\crystalkeep\ui\images\text_shard_node_icon.png"
inkscape:export-xdpi="192"
inkscape:export-ydpi="192">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="44.8"
inkscape:cx="8.2633984"
inkscape:cy="6.8552682"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
units="px" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76667)">
<path
sodipodi:type="star"
style="fill:#72deff;fill-opacity:1;stroke:none;stroke-width:0.07803907;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
id="path817"
sodipodi:sides="9"
sodipodi:cx="2.1166666"
sodipodi:cy="294.93815"
sodipodi:r1="1.8178079"
sodipodi:r2="1.2179312"
sodipodi:arg1="0.52359878"
sodipodi:arg2="0.87266463"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 3.6909344,295.84704 -0.7913968,0.0241 -0.1611441,0.77519 -0.6217269,-0.49025 -0.621727,0.49025 -0.1611441,-0.77519 -0.79139678,-0.0241 0.37483979,-0.69741 -0.59076328,-0.52715 0.73543197,-0.2933 -0.11370504,-0.78356 0.75190744,0.24804 0.416557,-0.67333 0.4165569,0.67333 0.7519075,-0.24804 -0.1137051,0.78356 0.735432,0.2933 -0.5907633,0.52715 z"
inkscape:transform-center-x="0.06028767"
inkscape:transform-center-y="0.030212626" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:1.9992249px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.12495156"
x="1.4333378"
y="295.61206"
id="text818"><tspan
sodipodi:role="line"
id="tspan816"
x="1.4333378"
y="295.61206"
style="stroke-width:0.12495156">A</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -5,4 +5,5 @@ module crystalkeep {
opens nl.andrewlalis.crystalkeep;
exports nl.andrewlalis.crystalkeep.control to javafx.fxml;
exports nl.andrewlalis.crystalkeep.model to javafx.fxml;
}

View File

@ -29,7 +29,15 @@ public class CrystalKeep extends Application {
stage.setScene(scene);
stage.setTitle("CrystalKeep");
stage.sizeToScene();
model.setActiveCluster(this.loadRootCluster());
stage.show();
MainViewController controller = loader.getController();
controller.init(model);
}
private Cluster loadRootCluster() throws IOException {
ClusterLoader clusterLoader = new ClusterLoader();
Cluster rootCluster;
try {
@ -45,12 +53,6 @@ public class CrystalKeep extends Application {
clusterLoader.saveDefault(rootCluster);
System.out.println("Saved root cluster on first load.");
}
model.setActiveCluster(rootCluster);
stage.show();
MainViewController controller = loader.getController();
controller.init(model);
System.out.println(rootCluster);
return rootCluster;
}
}

View File

@ -1,9 +0,0 @@
package nl.andrewlalis.crystalkeep.control;
import javafx.fxml.FXML;
import javafx.scene.control.TreeView;
public class ClusterTreeViewController {
@FXML
TreeView<String> clusterTreeView;
}

View File

@ -2,8 +2,6 @@ package nl.andrewlalis.crystalkeep.control;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.VBox;
@ -14,10 +12,21 @@ import nl.andrewlalis.crystalkeep.model.shards.TextShard;
import nl.andrewlalis.crystalkeep.view.ClusterTreeItem;
import nl.andrewlalis.crystalkeep.view.CrystalItemTreeCell;
import nl.andrewlalis.crystalkeep.view.ShardTreeItem;
import nl.andrewlalis.crystalkeep.view.shard_details.LoginCredentialsPane;
import nl.andrewlalis.crystalkeep.view.shard_details.ShardPane;
import nl.andrewlalis.crystalkeep.view.shard_details.TextShardPane;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class MainViewController implements ModelListener {
private static final Map<Class<? extends Shard>, Class<? extends ShardPane<? extends Shard>>> shardPanesMap = new HashMap<>();
static {
shardPanesMap.put(TextShard.class, TextShardPane.class);
shardPanesMap.put(LoginCredentialsShard.class, LoginCredentialsPane.class);
}
private Model model;
@FXML
@ -30,17 +39,17 @@ public class MainViewController implements ModelListener {
this.model.addListener(this);
this.activeClusterUpdated();
assert(this.clusterTreeView != null);
this.clusterTreeView.setCellFactory(param -> new CrystalItemTreeCell());
this.clusterTreeView.setCellFactory(param -> new CrystalItemTreeCell(this.model));
this.clusterTreeView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
shardDetailContainer.getChildren().clear();
if (newValue instanceof ShardTreeItem) {
var node = (ShardTreeItem) newValue;
System.out.println(node.getShard());
if (node.getShard() instanceof TextShard) {
shardDetailContainer.getChildren().add(new TextArea(((TextShard) node.getShard()).getText()));
} else if (node.getShard() instanceof LoginCredentialsShard) {
shardDetailContainer.getChildren().add(new Label("Username: " + ((LoginCredentialsShard) node.getShard()).getUsername()));
shardDetailContainer.getChildren().add(new Label("Password: " + ((LoginCredentialsShard) node.getShard()).getPassword()));
var paneClass = shardPanesMap.get(node.getShard().getClass());
try {
var pane = paneClass.getDeclaredConstructor(node.getShard().getClass()).newInstance(node.getShard());
shardDetailContainer.getChildren().add(pane);
} catch (Exception e) {
e.printStackTrace();
}
}
});
@ -66,6 +75,7 @@ public class MainViewController implements ModelListener {
private TreeItem<CrystalItem> createNode(Cluster cluster) {
ClusterTreeItem node = new ClusterTreeItem(cluster);
node.setExpanded(true);
for (Cluster child : cluster.getClustersOrdered()) {
node.getChildren().add(this.createNode(child));
}

View File

@ -38,10 +38,18 @@ public class Cluster implements Comparable<Cluster>, CrystalItem {
this.shards.add(shard);
}
public void removeShard(Shard shard) {
this.shards.remove(shard);
}
public void addCluster(Cluster cluster) {
this.clusters.add(cluster);
}
public void removeCluster(Cluster cluster) {
this.clusters.remove(cluster);
}
public List<Cluster> getClustersOrdered() {
List<Cluster> clusters = new ArrayList<>(this.getClusters());
clusters.sort(Comparator.naturalOrder());

View File

@ -19,6 +19,10 @@ public class Model {
public void setActiveCluster(Cluster activeCluster) {
this.activeCluster = activeCluster;
this.notifyListeners();
}
public void notifyListeners() {
this.listeners.forEach(ModelListener::activeClusterUpdated);
}
}

View File

@ -1,14 +1,11 @@
package nl.andrewlalis.crystalkeep.model;
import nl.andrewlalis.crystalkeep.model.shards.ShardType;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* A shard is a single "piece" of information, such as a snippet of text, login
* credentials, an image, or a private key. All shards within a cluster should
* have unique names.
* credentials, an image, or a private key.
* <p>
* Due to the need to deserialize shards from byte arrays, it is required
* that this parent class holds a type discriminator value, which is used to
@ -55,7 +52,7 @@ public abstract class Shard implements Comparable<Shard>, CrystalItem {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Shard shard = (Shard) o;
return getName().equals(shard.getName()) && getType() == shard.getType();
return getName().equals(shard.getName()) && getType() == shard.getType() && getCreatedAt().equals(shard.getCreatedAt());
}
@Override

View File

@ -1,6 +1,7 @@
package nl.andrewlalis.crystalkeep.model.shards;
package nl.andrewlalis.crystalkeep.model;
import nl.andrewlalis.crystalkeep.model.Shard;
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
/**
* Represents a distinct type of shard, and should correspond to exactly one

View File

@ -3,7 +3,7 @@ package nl.andrewlalis.crystalkeep.model.serialization;
import nl.andrewlalis.crystalkeep.model.Cluster;
import nl.andrewlalis.crystalkeep.model.Shard;
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
import nl.andrewlalis.crystalkeep.model.shards.ShardType;
import nl.andrewlalis.crystalkeep.model.ShardType;
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
import java.io.IOException;
@ -12,7 +12,6 @@ import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

View File

@ -1,6 +1,7 @@
package nl.andrewlalis.crystalkeep.model.shards;
import nl.andrewlalis.crystalkeep.model.Shard;
import nl.andrewlalis.crystalkeep.model.ShardType;
import nl.andrewlalis.crystalkeep.model.serialization.ByteUtils;
import nl.andrewlalis.crystalkeep.model.serialization.ShardSerializer;
@ -39,6 +40,11 @@ public class LoginCredentialsShard extends Shard {
return super.toString() + ", username=\"" + this.username + "\", password=\"" + this.password + "\"";
}
@Override
public String getIconPath() {
return "/nl/andrewlalis/crystalkeep/ui/images/login_credentials_shard_node_icon.png";
}
public static class Serializer implements ShardSerializer<LoginCredentialsShard> {
@Override

View File

@ -1,6 +1,7 @@
package nl.andrewlalis.crystalkeep.model.shards;
import nl.andrewlalis.crystalkeep.model.Shard;
import nl.andrewlalis.crystalkeep.model.ShardType;
import nl.andrewlalis.crystalkeep.model.serialization.ByteUtils;
import nl.andrewlalis.crystalkeep.model.serialization.ShardSerializer;
@ -29,6 +30,11 @@ public class TextShard extends Shard {
return super.toString() + ", text=\"" + this.text + "\"";
}
@Override
public String getIconPath() {
return "/nl/andrewlalis/crystalkeep/ui/images/text_shard_node_icon.png";
}
public static class Serializer implements ShardSerializer<TextShard> {
@Override
public byte[] serialize(TextShard shard) throws IOException {

View File

@ -5,35 +5,54 @@ import javafx.scene.control.MenuItem;
import javafx.scene.control.TreeCell;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import nl.andrewlalis.crystalkeep.model.Cluster;
import nl.andrewlalis.crystalkeep.model.CrystalItem;
import nl.andrewlalis.crystalkeep.model.Model;
import nl.andrewlalis.crystalkeep.model.Shard;
import java.io.InputStream;
public class CrystalItemTreeCell extends TreeCell<CrystalItem> {
private final ContextMenu contextMenu;
private final Model model;
public CrystalItemTreeCell() {
MenuItem item = new MenuItem("Delete");
this.contextMenu = new ContextMenu(item);
public CrystalItemTreeCell(Model model) {
this.model = model;
}
@Override
protected void updateItem(CrystalItem item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
ContextMenu menu = new ContextMenu();
if (item instanceof Cluster) {
var addShardItem = new MenuItem("Add Shard");
var addClusterItem = new MenuItem("Add Cluster");
menu.getItems().addAll(addShardItem, addClusterItem);
}
var deleteItem = new MenuItem("Delete");
deleteItem.setOnAction(event -> {
if (this.getTreeItem().getParent() != null && this.getTreeItem().getParent() instanceof ClusterTreeItem) {
var cluster = ((ClusterTreeItem) this.getTreeItem().getParent()).getCluster();
if (item instanceof Shard) {
cluster.removeShard((Shard) item);
} else if (item instanceof Cluster) {
cluster.removeCluster((Cluster) item);
}
this.model.notifyListeners();
}
});
menu.getItems().add(deleteItem);
this.setText(item.getName());
InputStream is = getClass().getResourceAsStream(item.getIconPath());
if (is != null) {
ImageView icon = new ImageView(new Image(is));
icon.setFitHeight(16);
icon.setFitHeight(24);
icon.setPreserveRatio(true);
this.setGraphic(icon);
}
if (item instanceof Shard) {
this.setContextMenu(this.contextMenu);
}
this.setContextMenu(menu);
} else {
this.setText(null);
this.setGraphic(null);

View File

@ -0,0 +1,56 @@
package nl.andrewlalis.crystalkeep.view.shard_details;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
public class LoginCredentialsPane extends ShardPane<LoginCredentialsShard> {
public LoginCredentialsPane(LoginCredentialsShard shard) {
super(shard);
}
@Override
protected Node getContent(LoginCredentialsShard shard) {
GridPane gp = new GridPane();
gp.setPadding(new Insets(5));
gp.setHgap(5);
gp.setVgap(5);
gp.add(new Label("Username"), 0, 0);
var usernameField = new TextField(shard.getUsername());
usernameField.textProperty().addListener((observable, oldValue, newValue) -> {
shard.setUsername(newValue);
});
gp.add(usernameField, 1, 0);
gp.add(new Label("Password"), 0, 1);
var passwordField = new PasswordField();
passwordField.setText(shard.getPassword());
var rawPasswordField = new TextField(shard.getPassword());
rawPasswordField.setVisible(false);
passwordField.textProperty().addListener((observable, oldValue, newValue) -> {
shard.setPassword(newValue);
rawPasswordField.setText(newValue);
});
rawPasswordField.textProperty().addListener((observable, oldValue, newValue) -> {
shard.setPassword(newValue);
passwordField.setText(newValue);
});
var passwordsContainer = new Pane();
passwordsContainer.getChildren().add(passwordField);
passwordsContainer.getChildren().add(rawPasswordField);
gp.add(passwordsContainer, 1, 1);
var showPasswordCheckbox = new CheckBox("Show password");
showPasswordCheckbox.setSelected(false);
showPasswordCheckbox.selectedProperty().addListener((observable, oldValue, newValue) -> {
passwordField.setVisible(!newValue);
rawPasswordField.setVisible(newValue);
});
gp.add(showPasswordCheckbox, 1, 2);
return gp;
}
}

View File

@ -0,0 +1,38 @@
package nl.andrewlalis.crystalkeep.view.shard_details;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import nl.andrewlalis.crystalkeep.model.Shard;
import java.time.format.DateTimeFormatter;
public abstract class ShardPane<T extends Shard> extends VBox {
public ShardPane(T shard) {
this.setSpacing(5);
GridPane gp = new GridPane();
gp.setPadding(new Insets(5));
gp.setHgap(5);
gp.setVgap(5);
gp.add(new Label("Name"), 0, 0);
var nameField = new TextField(shard.getName());
nameField.textProperty().addListener((observable, oldValue, newValue) -> {
shard.setName(newValue);
});
gp.add(nameField, 1, 0);
gp.add(new Label("Created at"), 0, 1);
var createdAtField = new TextField(shard.getCreatedAt().format(DateTimeFormatter.ofPattern("dd MMMM yyyy HH:mm:ss")));
createdAtField.setEditable(false);
gp.add(createdAtField, 1, 1);
this.getChildren().add(gp);
this.getChildren().add(new Separator(Orientation.HORIZONTAL));
this.getChildren().add(this.getContent(shard));
}
protected abstract Node getContent(T shard);
}

View File

@ -0,0 +1,20 @@
package nl.andrewlalis.crystalkeep.view.shard_details;
import javafx.scene.Node;
import javafx.scene.control.TextArea;
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
public class TextShardPane extends ShardPane<TextShard> {
public TextShardPane(TextShard shard) {
super(shard);
}
@Override
protected Node getContent(TextShard shard) {
var textArea = new TextArea(shard.getText());
textArea.textProperty().addListener((observable, oldValue, newValue) -> {
shard.setText(newValue);
});
return textArea;
}
}

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="nl.andrewlalis.crystalkeep.control.ClusterTreeViewController"
prefWidth="200.0"
>
<top>
<Label text="Clusters" BorderPane.alignment="TOP_CENTER"/>
</top>
<center>
<TreeView fx:id="clusterTreeView" prefWidth="200.0"/>
</center>
</BorderPane>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB