From c115f6e7290568eae6c0082f3afc9c7309f53268 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Thu, 28 May 2020 11:42:42 +0200 Subject: [PATCH] receive and address pane improvements --- drongo | 2 +- .../sparrow/control/AddressTreeTable.java | 14 ++-- .../sparrow/control/RecursiveTreeItem.java | 72 +++++++++++++++++++ .../sparrow/event/ReceiveActionEvent.java | 2 +- .../sparrow/event/ReceiveToEvent.java | 15 ++++ .../com/sparrowwallet/sparrow/io/Storage.java | 45 +++++++++++- .../sparrowwallet/sparrow/wallet/Entry.java | 13 ++-- .../sparrow/wallet/NodeEntry.java | 4 +- .../sparrow/wallet/ReceiveController.java | 15 +++- .../sparrow/wallet/WalletController.java | 6 ++ .../sparrow/wallet/WalletForm.java | 5 -- .../sparrowwallet/sparrow/wallet/receive.css | 5 ++ .../sparrowwallet/sparrow/wallet/receive.fxml | 22 ++++-- 13 files changed, 189 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/sparrow/control/RecursiveTreeItem.java create mode 100644 src/main/java/com/sparrowwallet/sparrow/event/ReceiveToEvent.java diff --git a/drongo b/drongo index 78714135..11978e1f 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 7871413573e67ed7539cf03d6deadd1a2c4abafa +Subproject commit 11978e1f48851cd964f1c5f52a29a8e2ea432432 diff --git a/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java b/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java index c6c907c1..885abd8c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java @@ -5,8 +5,10 @@ import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.event.ReceiveActionEvent; +import com.sparrowwallet.sparrow.event.ReceiveToEvent; import com.sparrowwallet.sparrow.wallet.Entry; import com.sparrowwallet.sparrow.wallet.NodeEntry; +import javafx.application.Platform; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.event.Event; import javafx.geometry.Pos; @@ -27,16 +29,11 @@ public class AddressTreeTable extends TreeTableView { public void initialize(NodeEntry rootEntry) { getStyleClass().add("address-treetable"); - String address = null; - TreeItem rootItem = new TreeItem<>(rootEntry); - for(Entry childEntry : rootEntry.getChildren()) { - TreeItem childItem = new TreeItem<>(childEntry); - rootItem.getChildren().add(childItem); - address = rootEntry.getNode().getAddress().toString(); - } + String address = rootEntry.getNode().getAddress().toString(); + RecursiveTreeItem rootItem = new RecursiveTreeItem<>(rootEntry, Entry::getChildren); + setRoot(rootItem); rootItem.setExpanded(true); - setRoot(rootItem); setShowRoot(false); TreeTableColumn addressCol = new TreeTableColumn<>("Address / Outpoints"); @@ -249,6 +246,7 @@ public class AddressTreeTable extends TreeTableView { receiveButton.setOnAction(event -> { NodeEntry nodeEntry = (NodeEntry)getTreeTableView().getTreeItem(getIndex()).getValue(); EventManager.get().post(new ReceiveActionEvent(nodeEntry)); + Platform.runLater(() -> EventManager.get().post(new ReceiveToEvent(nodeEntry))); }); } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/RecursiveTreeItem.java b/src/main/java/com/sparrowwallet/sparrow/control/RecursiveTreeItem.java new file mode 100644 index 00000000..4b8ecc9c --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/control/RecursiveTreeItem.java @@ -0,0 +1,72 @@ +package com.sparrowwallet.sparrow.control; + +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.scene.Node; +import javafx.scene.control.TreeItem; +import javafx.util.Callback; + +import java.util.List; +import java.util.stream.Collectors; + +public class RecursiveTreeItem extends TreeItem { + private final Callback> childrenFactory; + private final Callback graphicsFactory; + + public RecursiveTreeItem(Callback> childrenFactory){ + this(null, childrenFactory); + } + + public RecursiveTreeItem(final T value, Callback> childrenFactory){ + this(value, (item) -> null, childrenFactory); + } + + public RecursiveTreeItem(final T value, Callback graphicsFactory, Callback> childrenFactory){ + super(value, graphicsFactory.call(value)); + + this.graphicsFactory = graphicsFactory; + this.childrenFactory = childrenFactory; + + if(value != null) { + addChildrenListener(value); + } + + valueProperty().addListener((obs, oldValue, newValue)->{ + if(newValue != null){ + addChildrenListener(newValue); + } + }); + + this.setExpanded(true); + } + + private void addChildrenListener(T value){ + final ObservableList children = childrenFactory.call(value); + + children.forEach(child -> RecursiveTreeItem.this.getChildren().add( + new RecursiveTreeItem<>(child, this.graphicsFactory, childrenFactory))); + + children.addListener((ListChangeListener) change -> { + while(change.next()){ + + if(change.wasAdded()){ + change.getAddedSubList().forEach(t-> RecursiveTreeItem.this.getChildren().add( + new RecursiveTreeItem<>(t, this.graphicsFactory, childrenFactory))); + } + + if(change.wasRemoved()){ + change.getRemoved().forEach(t->{ + final List> itemsToRemove = RecursiveTreeItem.this + .getChildren() + .stream() + .filter(treeItem -> treeItem.getValue().equals(t)) + .collect(Collectors.toList()); + + RecursiveTreeItem.this.getChildren().removeAll(itemsToRemove); + }); + } + + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/sparrowwallet/sparrow/event/ReceiveActionEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/ReceiveActionEvent.java index 7a26c343..b842c3e7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/event/ReceiveActionEvent.java +++ b/src/main/java/com/sparrowwallet/sparrow/event/ReceiveActionEvent.java @@ -3,7 +3,7 @@ package com.sparrowwallet.sparrow.event; import com.sparrowwallet.sparrow.wallet.NodeEntry; public class ReceiveActionEvent { - private NodeEntry receiveEntry; + private final NodeEntry receiveEntry; public ReceiveActionEvent(NodeEntry receiveEntry) { this.receiveEntry = receiveEntry; diff --git a/src/main/java/com/sparrowwallet/sparrow/event/ReceiveToEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/ReceiveToEvent.java new file mode 100644 index 00000000..a57706d2 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/event/ReceiveToEvent.java @@ -0,0 +1,15 @@ +package com.sparrowwallet.sparrow.event; + +import com.sparrowwallet.sparrow.wallet.NodeEntry; + +public class ReceiveToEvent { + private final NodeEntry receiveEntry; + + public ReceiveToEvent(NodeEntry receiveEntry) { + this.receiveEntry = receiveEntry; + } + + public NodeEntry getReceiveEntry() { + return receiveEntry; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java index 8e1e558b..b3bde7d8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java @@ -18,6 +18,8 @@ import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.Arrays; import java.util.Base64; +import java.util.Iterator; +import java.util.TreeSet; import java.util.zip.*; import static com.sparrowwallet.drongo.crypto.Argon2KeyDeriver.SPRW1_PARAMETERS; @@ -49,14 +51,16 @@ public class Storage { return getGson(true); } - private static Gson getGson(boolean includeKeystoreSerializer) { + private static Gson getGson(boolean includeWalletSerializers) { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeySerializer()); gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer()); gsonBuilder.registerTypeAdapter(byte[].class, new ByteArraySerializer()); gsonBuilder.registerTypeAdapter(byte[].class, new ByteArrayDeserializer()); - if(includeKeystoreSerializer) { + if(includeWalletSerializers) { gsonBuilder.registerTypeAdapter(Keystore.class, new KeystoreSerializer()); + gsonBuilder.registerTypeAdapter(Wallet.Node.class, new NodeSerializer()); + gsonBuilder.registerTypeAdapter(Wallet.Node.class, new NodeDeserializer()); } return gsonBuilder.setPrettyPrinting().disableHtmlEscaping().create(); @@ -266,7 +270,6 @@ public class Storage { private static class KeystoreSerializer implements JsonSerializer { @Override public JsonElement serialize(Keystore keystore, Type typeOfSrc, JsonSerializationContext context) { - JsonObject jsonObject = (JsonObject)getGson(false).toJsonTree(keystore); if(keystore.hasSeed()) { jsonObject.remove("extendedPublicKey"); @@ -277,6 +280,42 @@ public class Storage { } } + private static class NodeSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Wallet.Node node, Type typeOfSrc, JsonSerializationContext context) { + JsonObject jsonObject = (JsonObject)getGson(false).toJsonTree(node); + + JsonArray children = jsonObject.getAsJsonArray("children"); + Iterator iter = children.iterator(); + while(iter.hasNext()) { + JsonObject childObject = (JsonObject)iter.next(); + if(childObject.get("label") == null) { + iter.remove(); + } + + if(childObject.get("children") != null && childObject.getAsJsonArray("children").size() == 0) { + childObject.remove("children"); + } + } + + return jsonObject; + } + } + + private static class NodeDeserializer implements JsonDeserializer { + @Override + public Wallet.Node deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + Wallet.Node node = getGson(false).fromJson(json, typeOfT); + for(Wallet.Node childNode : node.getChildren()) { + if(childNode.getChildren() == null) { + childNode.setChildren(new TreeSet<>()); + } + } + + return node; + } + } + public static class WalletAndKey { public final Wallet wallet; public final Key key; diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/Entry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/Entry.java index 3ad3d426..bdb53280 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/Entry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/Entry.java @@ -1,20 +1,23 @@ package com.sparrowwallet.sparrow.wallet; import javafx.beans.property.SimpleStringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; -import java.util.ArrayList; import java.util.List; public abstract class Entry { private final SimpleStringProperty labelProperty; - private final List children = new ArrayList<>(); + private final ObservableList children; - public Entry(String label) { + public Entry(String label, List entries) { this.labelProperty = new SimpleStringProperty(label); + this.children = FXCollections.observableList(entries); } - public Entry(SimpleStringProperty labelProperty) { + public Entry(SimpleStringProperty labelProperty, ObservableList children) { this.labelProperty = labelProperty; + this.children = children; } public String getLabel() { @@ -25,7 +28,7 @@ public abstract class Entry { return labelProperty; } - public List getChildren() { + public ObservableList getChildren() { return children; } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java index 7a62925e..a56d4a46 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java @@ -2,11 +2,13 @@ package com.sparrowwallet.sparrow.wallet; import com.sparrowwallet.drongo.wallet.Wallet; +import java.util.stream.Collectors; + public class NodeEntry extends Entry { private final Wallet.Node node; public NodeEntry(Wallet.Node node) { - super(node.getLabel()); + super(node.getLabel(), node.getChildren().stream().map(NodeEntry::new).collect(Collectors.toList())); this.node = node; labelProperty().addListener((observable, oldValue, newValue) -> node.setLabel(newValue)); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/ReceiveController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/ReceiveController.java index 7cf36015..a13820c2 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/ReceiveController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/ReceiveController.java @@ -1,5 +1,6 @@ package com.sparrowwallet.sparrow.wallet; +import com.google.common.eventbus.Subscribe; import com.google.zxing.BarcodeFormat; import com.google.zxing.client.j2se.MatrixToImageConfig; import com.google.zxing.client.j2se.MatrixToImageWriter; @@ -9,8 +10,7 @@ import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.control.CopyableLabel; import com.sparrowwallet.sparrow.control.CopyableTextField; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; +import com.sparrowwallet.sparrow.event.ReceiveToEvent; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -43,6 +43,9 @@ public class ReceiveController extends WalletFormController implements Initializ @FXML private CodeArea scriptPubKeyArea; + @FXML + private CodeArea outputDescriptor; + private NodeEntry currentEntry; @Override @@ -78,6 +81,9 @@ public class ReceiveController extends WalletFormController implements Initializ scriptPubKeyArea.clear(); appendScript(scriptPubKeyArea, nodeEntry.getNode().getOutputScript(), null, null); + + outputDescriptor.clear(); + outputDescriptor.appendText(nodeEntry.getNode().getOutputDescriptor()); } private Image getQrCode(String address) { @@ -101,4 +107,9 @@ public class ReceiveController extends WalletFormController implements Initializ NodeEntry freshEntry = getWalletForm().getFreshNodeEntry(KeyPurpose.RECEIVE, currentEntry); setNodeEntry(freshEntry); } + + @Subscribe + public void receiveTo(ReceiveToEvent event) { + setNodeEntry(event.getReceiveEntry()); + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java index a91d42f5..b40d38c8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java @@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.wallet; import com.google.common.eventbus.Subscribe; import com.sparrowwallet.sparrow.AppController; import com.sparrowwallet.sparrow.EventManager; +import com.sparrowwallet.sparrow.event.ReceiveActionEvent; import com.sparrowwallet.sparrow.event.WalletChangedEvent; import javafx.application.Platform; import javafx.fxml.FXML; @@ -97,4 +98,9 @@ public class WalletController extends WalletFormController implements Initializa public void walletChanged(WalletChangedEvent event) { configure(walletForm.getWallet().isValid()); } + + @Subscribe + public void receiveAction(ReceiveActionEvent event) { + selectFunction(Function.RECEIVE); + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java index eb9b2546..7910cabc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java @@ -52,11 +52,6 @@ public class WalletForm { } else { Wallet.Node purposeNode = getWallet().getNode(keyPurpose); purposeEntry = new NodeEntry(purposeNode); - for(Wallet.Node childNode : purposeNode.getChildren()) { - NodeEntry childEntry = new NodeEntry(childNode); - purposeEntry.getChildren().add(childEntry); - } - accountEntries.add(purposeEntry); } diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/receive.css b/src/main/resources/com/sparrowwallet/sparrow/wallet/receive.css index 7c845271..20fa274f 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/receive.css +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/receive.css @@ -5,4 +5,9 @@ .qr-code { -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 0); -fx-padding: 20; +} + +.receive-form .form .fieldset:horizontal .label-container { + -fx-pref-width: 90px; + -fx-pref-height: 25px; } \ No newline at end of file diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/receive.fxml b/src/main/resources/com/sparrowwallet/sparrow/wallet/receive.fxml index 35e78e0b..1d747b6f 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/receive.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/receive.fxml @@ -20,13 +20,13 @@
- + - - + + @@ -34,7 +34,7 @@
- + @@ -56,7 +56,7 @@
- + @@ -66,6 +66,18 @@
+
+
+ + + + + + + +
+
+