diff --git a/drongo b/drongo index 08c159eb..488752c1 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 08c159ebadee78af391e83d91b6a453a28acbf3e +Subproject commit 488752c142765bacd0373390faccbdb11b47487a diff --git a/src/main/java/com/sparrowwallet/sparrow/BaseController.java b/src/main/java/com/sparrowwallet/sparrow/BaseController.java index 9547d7b0..f7a14f2b 100644 --- a/src/main/java/com/sparrowwallet/sparrow/BaseController.java +++ b/src/main/java/com/sparrowwallet/sparrow/BaseController.java @@ -1,6 +1,9 @@ package com.sparrowwallet.sparrow; +import com.sparrowwallet.drongo.policy.PolicyType; import com.sparrowwallet.drongo.protocol.ScriptChunk; +import com.sparrowwallet.drongo.wallet.Keystore; +import com.sparrowwallet.sparrow.control.DescriptorArea; import com.sparrowwallet.sparrow.control.ScriptArea; import javafx.geometry.Point2D; import javafx.scene.control.Label; @@ -39,4 +42,42 @@ public abstract class BaseController { protected String describeScriptChunk(ScriptChunk chunk) { return chunk.toString(); } + + protected void initializeDescriptorField(DescriptorArea descriptorArea) { + Popup popup = new Popup(); + Label popupMsg = new Label(); + popupMsg.getStyleClass().add("tooltip"); + popup.getContent().add(popupMsg); + + descriptorArea.setMouseOverTextDelay(Duration.ofMillis(150)); + descriptorArea.addEventHandler(MouseOverTextEvent.MOUSE_OVER_TEXT_BEGIN, e -> { + TwoDimensional.Position position = descriptorArea.getParagraph(0).getStyleSpans().offsetToPosition(e.getCharacterIndex(), Backward); + int index = descriptorArea.getWallet().getPolicyType() == PolicyType.SINGLE ? position.getMajor() - 1 : ((position.getMajor() - 1) / 2); + if(position.getMajor() > 0 && index >= 0 && index < descriptorArea.getWallet().getKeystores().size()) { + Keystore hoverKeystore = descriptorArea.getWallet().getKeystores().get(index); + Point2D pos = e.getScreenPosition(); + popupMsg.setText(describeKeystore(hoverKeystore)); + popup.show(descriptorArea, pos.getX(), pos.getY() + 10); + } + }); + descriptorArea.addEventHandler(MouseOverTextEvent.MOUSE_OVER_TEXT_END, e -> { + popup.hide(); + }); + } + + protected String describeKeystore(Keystore keystore) { + if(keystore.isValid()) { + StringBuilder builder = new StringBuilder(); + builder.append("["); + builder.append(keystore.getKeyDerivation().getMasterFingerprint()); + builder.append("/"); + builder.append(keystore.getKeyDerivation().getDerivationPath().replaceFirst("^m?/", "")); + builder.append("]"); + builder.append(keystore.getExtendedPublicKey().toString()); + + return builder.toString(); + } + + return "Invalid"; + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/DescriptorArea.java b/src/main/java/com/sparrowwallet/sparrow/control/DescriptorArea.java new file mode 100644 index 00000000..da23de9b --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/control/DescriptorArea.java @@ -0,0 +1,89 @@ +package com.sparrowwallet.sparrow.control; + +import com.sparrowwallet.drongo.OutputDescriptor; +import com.sparrowwallet.drongo.policy.PolicyType; +import com.sparrowwallet.drongo.protocol.ScriptType; +import com.sparrowwallet.drongo.wallet.Keystore; +import com.sparrowwallet.drongo.wallet.Wallet; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import org.fxmisc.richtext.CodeArea; + +import java.util.List; + +import static com.sparrowwallet.drongo.policy.PolicyType.MULTI; +import static com.sparrowwallet.drongo.policy.PolicyType.SINGLE; +import static com.sparrowwallet.drongo.protocol.ScriptType.MULTISIG; + +public class DescriptorArea extends CodeArea { + private Wallet wallet; + + public void setWallet(Wallet wallet) { + clear(); + this.wallet = wallet; + + DescriptorContextMenu contextMenu = new DescriptorContextMenu(wallet, this); + setContextMenu(contextMenu); + + PolicyType policyType = wallet.getPolicyType(); + ScriptType scriptType = wallet.getScriptType(); + List keystores = wallet.getKeystores(); + int threshold = wallet.getDefaultPolicy().getNumSignaturesRequired(); + + if(SINGLE.equals(policyType)) { + append(scriptType.getDescriptor(), "descriptor-text"); + replace(getLength(), getLength(), keystores.get(0).getScriptName(), List.of(keystores.get(0).isValid() ? "descriptor-text" : "descriptor-error", keystores.get(0).getScriptName())); + append(scriptType.getCloseDescriptor(), "descriptor-text"); + } + + if(MULTI.equals(policyType)) { + append(scriptType.getDescriptor(), "descriptor-text"); + append(MULTISIG.getDescriptor(), "descriptor-text"); + append(Integer.toString(threshold), "descriptor-text"); + + for(Keystore keystore : keystores) { + append(",", "descriptor-text"); + replace(getLength(), getLength(), keystore.getScriptName(), List.of(keystore.isValid() ? "descriptor-text" : "descriptor-error", keystore.getScriptName())); + } + + append(MULTISIG.getCloseDescriptor(), "descriptor-text"); + append(scriptType.getCloseDescriptor(), "descriptor-text"); + } + } + + public Wallet getWallet() { + return wallet; + } + + public void clear() { + super.clear(); + this.wallet = null; + setDisable(false); + setContextMenu(null); + } + + private static class DescriptorContextMenu extends ContextMenu { + public DescriptorContextMenu(Wallet wallet, DescriptorArea descriptorArea) { + MenuItem copyvalue = new MenuItem("Copy Value"); + copyvalue.setOnAction(AE -> { + hide(); + ClipboardContent content = new ClipboardContent(); + content.putString(descriptorArea.getText()); + Clipboard.getSystemClipboard().setContent(content); + }); + getItems().add(copyvalue); + + MenuItem copyOutputDescriptor = new MenuItem("Copy Output Descriptor"); + copyOutputDescriptor.setOnAction(AE -> { + hide(); + ClipboardContent content = new ClipboardContent(); + content.putString(OutputDescriptor.getOutputDescriptor(wallet).toString(true)); + Clipboard.getSystemClipboard().setContent(content); + }); + getItems().add(copyOutputDescriptor); + this.setStyle("-fx-background-color: -fx-color; -fx-font-family: System; -fx-font-size: 1em;"); + } + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/control/ScriptArea.java b/src/main/java/com/sparrowwallet/sparrow/control/ScriptArea.java index 5c4b55ca..ff66766d 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/ScriptArea.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/ScriptArea.java @@ -2,7 +2,6 @@ package com.sparrowwallet.sparrow.control; import com.sparrowwallet.drongo.protocol.Script; import com.sparrowwallet.drongo.protocol.ScriptChunk; -import com.sparrowwallet.sparrow.transaction.ScriptContextMenu; import javafx.geometry.Pos; import org.controlsfx.control.decoration.Decorator; import org.controlsfx.control.decoration.GraphicDecoration; diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/ScriptContextMenu.java b/src/main/java/com/sparrowwallet/sparrow/control/ScriptContextMenu.java similarity index 95% rename from src/main/java/com/sparrowwallet/sparrow/transaction/ScriptContextMenu.java rename to src/main/java/com/sparrowwallet/sparrow/control/ScriptContextMenu.java index bd6d97cf..b250aa68 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/ScriptContextMenu.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/ScriptContextMenu.java @@ -1,4 +1,4 @@ -package com.sparrowwallet.sparrow.transaction; +package com.sparrowwallet.sparrow.control; import com.sparrowwallet.drongo.protocol.Script; import com.sparrowwallet.drongo.protocol.ScriptChunk; @@ -33,7 +33,7 @@ public class ScriptContextMenu extends ContextMenu { }); getItems().add(copyvalue); - this.setStyle("-fx-background-color: -fx-color; -fx-font-family: sans-serif; -fx-font-size: 1em;"); + this.setStyle("-fx-background-color: -fx-color; -fx-font-family: System; -fx-font-size: 1em;"); area.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> { hoverChunk = null; diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java index 5c74f2af..c9b5b3a6 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java @@ -13,6 +13,7 @@ import com.sparrowwallet.drongo.wallet.WalletModel; import com.sparrowwallet.sparrow.AppController; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.control.CopyableLabel; +import com.sparrowwallet.sparrow.control.DescriptorArea; import com.sparrowwallet.sparrow.control.WalletPasswordDialog; import com.sparrowwallet.sparrow.event.SettingsChangedEvent; import com.sparrowwallet.sparrow.event.StorageEvent; @@ -46,7 +47,7 @@ public class SettingsController extends WalletFormController implements Initiali private ComboBox policyType; @FXML - private TextField spendingMiniscript; + private DescriptorArea descriptor; @FXML private ComboBox scriptType; @@ -160,6 +161,8 @@ public class SettingsController extends WalletFormController implements Initiali } }); + initializeDescriptorField(descriptor); + revert.setOnAction(event -> { keystoreTabs.getTabs().removeAll(keystoreTabs.getTabs()); totalKeystores.unbind(); @@ -224,6 +227,7 @@ public class SettingsController extends WalletFormController implements Initiali KeystoreController controller = keystoreLoader.getController(); controller.setKeystore(getWalletForm(), keystore); tab.textProperty().bind(controller.getLabel().textProperty()); + tab.setUserData(keystore); controller.getValidationSupport().validationResultProperty().addListener((o, oldValue, result) -> { if(result.getErrors().isEmpty()) { @@ -243,6 +247,19 @@ public class SettingsController extends WalletFormController implements Initiali } } + @Override + protected String describeKeystore(Keystore keystore) { + if(!keystore.isValid()) { + for(Tab tab : keystoreTabs.getTabs()) { + if(tab.getUserData() == keystore && tab.getTooltip() != null) { + return tab.getTooltip().getText(); + } + } + } + + return super.describeKeystore(keystore); + } + @Subscribe public void update(SettingsChangedEvent event) { Wallet wallet = event.getWallet(); @@ -253,7 +270,7 @@ public class SettingsController extends WalletFormController implements Initiali wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), (int)multisigControl.getLowValue())); } - spendingMiniscript.setText(wallet.getDefaultPolicy().getMiniscript().getScript()); + descriptor.setWallet(wallet); revert.setDisable(false); apply.setDisable(!wallet.isValid()); } diff --git a/src/main/resources/com/sparrowwallet/sparrow/descriptor.css b/src/main/resources/com/sparrowwallet/sparrow/descriptor.css new file mode 100644 index 00000000..4b1bfd52 --- /dev/null +++ b/src/main/resources/com/sparrowwallet/sparrow/descriptor.css @@ -0,0 +1,3 @@ +.descriptor-text { -fx-fill: #000000 } +.descriptor-error { -fx-fill: #ca1243 } + diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.css b/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.css index a113df66..ef616839 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.css +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.css @@ -10,8 +10,4 @@ -fx-alignment: center-left; } -#spendingMiniscript { - -fx-font-size: 13px; - -fx-font-family: 'Roboto Mono'; -} diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.fxml b/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.fxml index 3ac93dbe..122391c6 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.fxml @@ -9,8 +9,10 @@ + + - +
@@ -59,7 +61,7 @@
- + @@ -70,9 +72,13 @@
-
- - +
+ + + + + +