mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 13:16:44 +00:00
add figure caption to overview diagram on transaction tab to describe transaction
This commit is contained in:
parent
2b8fc3900a
commit
4e3e8b7cc4
10 changed files with 330 additions and 10 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 74d2bfec24204300392d7a750b6b010038fb9727
|
Subproject commit 30aff119081a4a13f931ea6625f69d7974addb04
|
|
@ -45,6 +45,7 @@ public class SparrowDesktop extends Application {
|
||||||
GlyphFontRegistry.register(new FontAwesome5());
|
GlyphFontRegistry.register(new FontAwesome5());
|
||||||
GlyphFontRegistry.register(new FontAwesome5Brands());
|
GlyphFontRegistry.register(new FontAwesome5Brands());
|
||||||
Font.loadFont(AppServices.class.getResourceAsStream("/font/RobotoMono-Regular.ttf"), 13);
|
Font.loadFont(AppServices.class.getResourceAsStream("/font/RobotoMono-Regular.ttf"), 13);
|
||||||
|
Font.loadFont(AppServices.class.getResourceAsStream("/font/RobotoMono-Italic.ttf"), 11);
|
||||||
URL.setURLStreamHandlerFactory(protocol -> WalletIcon.PROTOCOL.equals(protocol) ? new WalletIcon.WalletIconStreamHandler() : null);
|
URL.setURLStreamHandlerFactory(protocol -> WalletIcon.PROTOCOL.equals(protocol) ? new WalletIcon.WalletIconStreamHandler() : null);
|
||||||
|
|
||||||
AppServices.initialize(this);
|
AppServices.initialize(this);
|
||||||
|
|
|
@ -72,6 +72,7 @@ public class TransactionDiagram extends GridPane {
|
||||||
|
|
||||||
private WalletTransaction walletTx;
|
private WalletTransaction walletTx;
|
||||||
private final BooleanProperty finalProperty = new SimpleBooleanProperty(false);
|
private final BooleanProperty finalProperty = new SimpleBooleanProperty(false);
|
||||||
|
private final ObjectProperty<TransactionDiagramLabel> labelProperty = new SimpleObjectProperty<>(null);
|
||||||
private final ObjectProperty<OptimizationStrategy> optimizationStrategyProperty = new SimpleObjectProperty<>(OptimizationStrategy.EFFICIENCY);
|
private final ObjectProperty<OptimizationStrategy> optimizationStrategyProperty = new SimpleObjectProperty<>(OptimizationStrategy.EFFICIENCY);
|
||||||
private boolean expanded;
|
private boolean expanded;
|
||||||
private TransactionDiagram expandedDiagram;
|
private TransactionDiagram expandedDiagram;
|
||||||
|
@ -154,6 +155,10 @@ public class TransactionDiagram extends GridPane {
|
||||||
updateDerivedDiagram(expandedDiagram);
|
updateDerivedDiagram(expandedDiagram);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(getLabel() != null) {
|
||||||
|
getLabel().update(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(String message) {
|
public void update(String message) {
|
||||||
|
@ -534,7 +539,7 @@ public class TransactionDiagram extends GridPane {
|
||||||
return input.getLabel() != null && !input.getLabel().isEmpty() ? input.getLabel() : input.getHashAsString().substring(0, 8) + "..:" + input.getIndex();
|
return input.getLabel() != null && !input.getLabel().isEmpty() ? input.getLabel() : input.getHashAsString().substring(0, 8) + "..:" + input.getIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSatsValue(long amount) {
|
String getSatsValue(long amount) {
|
||||||
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
|
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
|
||||||
return format.formatSatsValue(amount);
|
return format.formatSatsValue(amount);
|
||||||
}
|
}
|
||||||
|
@ -923,12 +928,12 @@ public class TransactionDiagram extends GridPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(payment.getType() == Payment.Type.WHIRLPOOL_FEE) {
|
if(payment.getType() == Payment.Type.WHIRLPOOL_FEE) {
|
||||||
return "Whirlpool Fee";
|
return "Whirlpool fee";
|
||||||
} else if(walletTx.isPremixSend(payment)) {
|
} else if(walletTx.isPremixSend(payment)) {
|
||||||
int premixIndex = getOutputIndex(payment.getAddress(), payment.getAmount()) - 2;
|
int premixIndex = getOutputIndex(payment.getAddress(), payment.getAmount()) - 2;
|
||||||
return "Premix #" + premixIndex;
|
return "Premix #" + premixIndex;
|
||||||
} else if(walletTx.isBadbankSend(payment)) {
|
} else if(walletTx.isBadbankSend(payment)) {
|
||||||
return "Badbank Change";
|
return "Badbank change";
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -938,7 +943,7 @@ public class TransactionDiagram extends GridPane {
|
||||||
return walletTx.getTransaction().getOutputs().stream().filter(txOutput -> address.equals(txOutput.getScript().getToAddress()) && txOutput.getValue() == amount).mapToInt(TransactionOutput::getIndex).findFirst().orElseThrow();
|
return walletTx.getTransaction().getOutputs().stream().filter(txOutput -> address.equals(txOutput.getScript().getToAddress()) && txOutput.getValue() == amount).mapToInt(TransactionOutput::getIndex).findFirst().orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Wallet getToWallet(Payment payment) {
|
Wallet getToWallet(Payment payment) {
|
||||||
for(Wallet openWallet : AppServices.get().getOpenWallets().keySet()) {
|
for(Wallet openWallet : AppServices.get().getOpenWallets().keySet()) {
|
||||||
if(openWallet != walletTx.getWallet() && openWallet.isValid()) {
|
if(openWallet != walletTx.getWallet() && openWallet.isValid()) {
|
||||||
WalletNode addressNode = openWallet.getWalletAddresses().get(payment.getAddress());
|
WalletNode addressNode = openWallet.getWalletAddresses().get(payment.getAddress());
|
||||||
|
@ -1078,7 +1083,7 @@ public class TransactionDiagram extends GridPane {
|
||||||
return changeReplaceGlyph;
|
return changeReplaceGlyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Glyph getFeeGlyph() {
|
public Glyph getFeeGlyph() {
|
||||||
Glyph feeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.HAND_HOLDING);
|
Glyph feeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.HAND_HOLDING);
|
||||||
feeGlyph.getStyleClass().add("fee-icon");
|
feeGlyph.getStyleClass().add("fee-icon");
|
||||||
feeGlyph.setFontSize(12);
|
feeGlyph.setFontSize(12);
|
||||||
|
@ -1162,6 +1167,10 @@ public class TransactionDiagram extends GridPane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WalletTransaction getWalletTransaction() {
|
||||||
|
return walletTx;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isFinal() {
|
public boolean isFinal() {
|
||||||
return finalProperty.get();
|
return finalProperty.get();
|
||||||
}
|
}
|
||||||
|
@ -1174,6 +1183,18 @@ public class TransactionDiagram extends GridPane {
|
||||||
this.finalProperty.set(isFinal);
|
this.finalProperty.set(isFinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransactionDiagramLabel getLabel() {
|
||||||
|
return labelProperty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<TransactionDiagramLabel> labelProperty() {
|
||||||
|
return labelProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabelProperty(TransactionDiagramLabel label) {
|
||||||
|
this.labelProperty.set(label);
|
||||||
|
}
|
||||||
|
|
||||||
public OptimizationStrategy getOptimizationStrategy() {
|
public OptimizationStrategy getOptimizationStrategy() {
|
||||||
return optimizationStrategyProperty.get();
|
return optimizationStrategyProperty.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.text.Font;
|
||||||
|
import org.controlsfx.glyphfont.Glyph;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class TransactionDiagramLabel extends HBox {
|
||||||
|
private final List<HBox> outputs = new ArrayList<>();
|
||||||
|
private final Button left;
|
||||||
|
private final Button right;
|
||||||
|
private final IntegerProperty displayedIndex = new SimpleIntegerProperty(-1);
|
||||||
|
|
||||||
|
public TransactionDiagramLabel() {
|
||||||
|
setSpacing(5);
|
||||||
|
setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
|
left = new Button("");
|
||||||
|
left.setGraphic(getLeftGlyph());
|
||||||
|
left.setOnAction(event -> {
|
||||||
|
int index = displayedIndex.get();
|
||||||
|
if(index > 0) {
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
displayedIndex.set(index);
|
||||||
|
});
|
||||||
|
|
||||||
|
right = new Button("");
|
||||||
|
right.setGraphic(getRightGlyph());
|
||||||
|
right.setOnAction(event -> {
|
||||||
|
int index = displayedIndex.get();
|
||||||
|
if(index < outputs.size() - 1) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
displayedIndex.set(index);
|
||||||
|
});
|
||||||
|
|
||||||
|
displayedIndex.addListener((observable, oldValue, newValue) -> {
|
||||||
|
left.setDisable(newValue.intValue() <= 0);
|
||||||
|
right.setDisable(newValue.intValue() < 0 || newValue.intValue() >= outputs.size() - 1);
|
||||||
|
if(oldValue.intValue() >= 0 && oldValue.intValue() < outputs.size()) {
|
||||||
|
outputs.get(oldValue.intValue()).setVisible(false);
|
||||||
|
}
|
||||||
|
if(newValue.intValue() >= 0 && newValue.intValue() < outputs.size()) {
|
||||||
|
outputs.get(newValue.intValue()).setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(TransactionDiagram transactionDiagram) {
|
||||||
|
getChildren().clear();
|
||||||
|
outputs.clear();
|
||||||
|
displayedIndex.set(-1);
|
||||||
|
double maxWidth = getMaxWidth();
|
||||||
|
|
||||||
|
WalletTransaction walletTx = transactionDiagram.getWalletTransaction();
|
||||||
|
List<OutputLabel> outputLabels = new ArrayList<>();
|
||||||
|
|
||||||
|
List<Payment> premixOutputs = walletTx.getPayments().stream().filter(walletTx::isPremixSend).collect(Collectors.toList());
|
||||||
|
if(!premixOutputs.isEmpty()) {
|
||||||
|
OutputLabel premixOutputLabel = getPremixOutputLabel(transactionDiagram, premixOutputs);
|
||||||
|
if(premixOutputLabel != null) {
|
||||||
|
outputLabels.add(premixOutputLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Payment> optWhirlpoolFee = walletTx.getPayments().stream().filter(payment -> payment.getType() == Payment.Type.WHIRLPOOL_FEE).findFirst();
|
||||||
|
if(optWhirlpoolFee.isPresent()) {
|
||||||
|
OutputLabel whirlpoolFeeOutputLabel = getWhirlpoolFeeOutputLabel(transactionDiagram, optWhirlpoolFee.get(), premixOutputs);
|
||||||
|
outputLabels.add(whirlpoolFeeOutputLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Payment> badbankOutputs = walletTx.getPayments().stream().filter(walletTx::isBadbankSend).collect(Collectors.toList());
|
||||||
|
List<OutputLabel> badbankOutputLabels = badbankOutputs.stream().map(payment -> getBadbankOutputLabel(transactionDiagram, payment)).collect(Collectors.toList());
|
||||||
|
outputLabels.addAll(badbankOutputLabels);
|
||||||
|
} else if(walletTx.getPayments().size() >= 5 && walletTx.getPayments().stream().mapToLong(Payment::getAmount).distinct().count() <= 1
|
||||||
|
&& walletTx.getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_PREMIX && walletTx.getPayments().stream().anyMatch(walletTx::isPostmixSend)) {
|
||||||
|
OutputLabel mixOutputLabel = getMixOutputLabel(transactionDiagram, walletTx.getPayments());
|
||||||
|
if(mixOutputLabel != null) {
|
||||||
|
outputLabels.add(mixOutputLabel);
|
||||||
|
}
|
||||||
|
} else if(walletTx.getPayments().size() >= 5 && walletTx.getPayments().stream().mapToLong(Payment::getAmount).distinct().count() <= 1
|
||||||
|
&& walletTx.getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX && walletTx.getPayments().stream().anyMatch(walletTx::isConsolidationSend)) {
|
||||||
|
OutputLabel remixOutputLabel = getRemixOutputLabel(transactionDiagram, walletTx.getPayments());
|
||||||
|
if(remixOutputLabel != null) {
|
||||||
|
outputLabels.add(remixOutputLabel);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List<Payment> payments = walletTx.getPayments().stream().filter(payment -> payment.getType() == Payment.Type.DEFAULT && !walletTx.isConsolidationSend(payment)).collect(Collectors.toList());
|
||||||
|
List<OutputLabel> paymentLabels = payments.stream().map(payment -> getOutputLabel(transactionDiagram, payment)).collect(Collectors.toList());
|
||||||
|
if(walletTx.getSelectedUtxos().values().stream().allMatch(Objects::isNull)) {
|
||||||
|
paymentLabels.sort(Comparator.comparingInt(paymentLabel -> (paymentLabel.text.startsWith("Receive") ? 0 : 1)));
|
||||||
|
}
|
||||||
|
outputLabels.addAll(paymentLabels);
|
||||||
|
|
||||||
|
List<Payment> consolidations = walletTx.getPayments().stream().filter(payment -> payment.getType() == Payment.Type.DEFAULT && walletTx.isConsolidationSend(payment)).collect(Collectors.toList());
|
||||||
|
outputLabels.addAll(consolidations.stream().map(consolidation -> getOutputLabel(transactionDiagram, consolidation)).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
List<Payment> mixes = walletTx.getPayments().stream().filter(payment -> payment.getType() == Payment.Type.MIX || payment.getType() == Payment.Type.FAKE_MIX).collect(Collectors.toList());
|
||||||
|
outputLabels.addAll(mixes.stream().map(payment -> getOutputLabel(transactionDiagram, payment)).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<WalletNode, Long> changeMap = walletTx.getChangeMap();
|
||||||
|
outputLabels.addAll(changeMap.entrySet().stream().map(changeEntry -> getOutputLabel(transactionDiagram, changeEntry)).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
OutputLabel feeOutputLabel = getFeeOutputLabel(transactionDiagram);
|
||||||
|
if(feeOutputLabel != null) {
|
||||||
|
outputLabels.add(feeOutputLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(OutputLabel outputLabel : outputLabels) {
|
||||||
|
maxWidth = Math.max(maxWidth, outputLabel.width);
|
||||||
|
outputs.add(outputLabel.hBox);
|
||||||
|
getChildren().add(outputLabel.hBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
HBox buttonBox = new HBox();
|
||||||
|
buttonBox.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
buttonBox.getChildren().addAll(left, right);
|
||||||
|
getChildren().add(buttonBox);
|
||||||
|
|
||||||
|
setMaxWidth(maxWidth);
|
||||||
|
setPrefWidth(maxWidth);
|
||||||
|
|
||||||
|
if(outputLabels.size() > 0) {
|
||||||
|
displayedIndex.set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputLabel getPremixOutputLabel(TransactionDiagram transactionDiagram, List<Payment> premixOutputs) {
|
||||||
|
if(premixOutputs.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Payment premixOutput = premixOutputs.get(0);
|
||||||
|
long total = premixOutputs.stream().mapToLong(Payment::getAmount).sum();
|
||||||
|
Glyph glyph = transactionDiagram.getOutputGlyph(premixOutput);
|
||||||
|
String text;
|
||||||
|
if(premixOutputs.size() == 1) {
|
||||||
|
text = "Premix transaction with 1 output of " + transactionDiagram.getSatsValue(premixOutput.getAmount()) + " sats";
|
||||||
|
} else {
|
||||||
|
text = "Premix transaction with " + premixOutputs.size() + " outputs of " + transactionDiagram.getSatsValue(premixOutput.getAmount()) + " sats each ("
|
||||||
|
+ transactionDiagram.getSatsValue(total) + " sats)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return getOutputLabel(glyph, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputLabel getBadbankOutputLabel(TransactionDiagram transactionDiagram, Payment payment) {
|
||||||
|
Glyph glyph = transactionDiagram.getOutputGlyph(payment);
|
||||||
|
String text = "Badbank change of " + transactionDiagram.getSatsValue(payment.getAmount()) + " sats to " + payment.getAddress().toString();
|
||||||
|
|
||||||
|
return getOutputLabel(glyph, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputLabel getWhirlpoolFeeOutputLabel(TransactionDiagram transactionDiagram, Payment whirlpoolFee, List<Payment> premixOutputs) {
|
||||||
|
long total = premixOutputs.stream().mapToLong(Payment::getAmount).sum();
|
||||||
|
double feePercentage = (double)whirlpoolFee.getAmount() / (total - whirlpoolFee.getAmount());
|
||||||
|
Glyph glyph = transactionDiagram.getOutputGlyph(whirlpoolFee);
|
||||||
|
String text = "Whirlpool fee of " + transactionDiagram.getSatsValue(whirlpoolFee.getAmount()) + " sats (" + String.format("%.2f", feePercentage * 100.0) + "% of total premix value)";
|
||||||
|
|
||||||
|
return getOutputLabel(glyph, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputLabel getMixOutputLabel(TransactionDiagram transactionDiagram, List<Payment> mixOutputs) {
|
||||||
|
if(mixOutputs.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Payment remixOutput = mixOutputs.get(0);
|
||||||
|
long total = mixOutputs.stream().mapToLong(Payment::getAmount).sum();
|
||||||
|
Glyph glyph = TransactionDiagram.getPremixGlyph();
|
||||||
|
String text = "Mix transaction with " + mixOutputs.size() + " outputs of " + transactionDiagram.getSatsValue(remixOutput.getAmount()) + " sats each ("
|
||||||
|
+ transactionDiagram.getSatsValue(total) + " sats)";
|
||||||
|
|
||||||
|
return getOutputLabel(glyph, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputLabel getRemixOutputLabel(TransactionDiagram transactionDiagram, List<Payment> remixOutputs) {
|
||||||
|
if(remixOutputs.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Payment remixOutput = remixOutputs.get(0);
|
||||||
|
long total = remixOutputs.stream().mapToLong(Payment::getAmount).sum();
|
||||||
|
Glyph glyph = TransactionDiagram.getPremixGlyph();
|
||||||
|
String text = "Remix transaction with " + remixOutputs.size() + " outputs of " + transactionDiagram.getSatsValue(remixOutput.getAmount()) + " sats each ("
|
||||||
|
+ transactionDiagram.getSatsValue(total) + " sats)";
|
||||||
|
|
||||||
|
return getOutputLabel(glyph, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputLabel getOutputLabel(TransactionDiagram transactionDiagram, Payment payment) {
|
||||||
|
WalletTransaction walletTx = transactionDiagram.getWalletTransaction();
|
||||||
|
Wallet toWallet = transactionDiagram.getToWallet(payment);
|
||||||
|
WalletNode toNode = walletTx.getWallet() != null && !walletTx.getWallet().isBip47() ? walletTx.getAddressNodeMap().get(payment.getAddress()) : null;
|
||||||
|
|
||||||
|
Glyph glyph = transactionDiagram.getOutputGlyph(payment);
|
||||||
|
String text = (toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ") + transactionDiagram.getSatsValue(payment.getAmount()) + " sats to " + payment.getAddress().toString();
|
||||||
|
|
||||||
|
return getOutputLabel(glyph, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputLabel getOutputLabel(TransactionDiagram transactionDiagram, Map.Entry<WalletNode, Long> changeEntry) {
|
||||||
|
WalletTransaction walletTx = transactionDiagram.getWalletTransaction();
|
||||||
|
|
||||||
|
Glyph glyph = TransactionDiagram.getChangeGlyph();
|
||||||
|
String text = "Change of " + transactionDiagram.getSatsValue(changeEntry.getValue()) + " sats to " + walletTx.getChangeAddress(changeEntry.getKey()).toString();
|
||||||
|
|
||||||
|
return getOutputLabel(glyph, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputLabel getFeeOutputLabel(TransactionDiagram transactionDiagram) {
|
||||||
|
WalletTransaction walletTx = transactionDiagram.getWalletTransaction();
|
||||||
|
if(walletTx.getFee() < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Glyph glyph = transactionDiagram.getFeeGlyph();
|
||||||
|
String text = "Fee of " + transactionDiagram.getSatsValue(walletTx.getFee()) + " sats (" + String.format("%.2f", walletTx.getFeePercentage() * 100.0) + "%)";
|
||||||
|
|
||||||
|
return getOutputLabel(glyph, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputLabel getOutputLabel(Glyph glyph, String text) {
|
||||||
|
Label icon = new Label();
|
||||||
|
icon.setMinWidth(15);
|
||||||
|
glyph.setFontSize(12);
|
||||||
|
icon.setGraphic(glyph);
|
||||||
|
|
||||||
|
CopyableLabel label = new CopyableLabel();
|
||||||
|
label.setFont(Font.font("Roboto Mono Italic", 13));
|
||||||
|
label.setText(text);
|
||||||
|
|
||||||
|
HBox output = new HBox(5);
|
||||||
|
output.setAlignment(Pos.CENTER);
|
||||||
|
output.managedProperty().bind(output.visibleProperty());
|
||||||
|
output.setVisible(false);
|
||||||
|
output.getChildren().addAll(icon, label);
|
||||||
|
|
||||||
|
double lineWidth = TextUtils.computeTextWidth(label.getFont(), label.getText(), 0.0D) + 2 + getSpacing() + icon.getMinWidth() + 60;
|
||||||
|
return new OutputLabel(output, lineWidth, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Glyph getLeftGlyph() {
|
||||||
|
Glyph caretLeftGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.CARET_LEFT);
|
||||||
|
caretLeftGlyph.getStyleClass().add("label-left-icon");
|
||||||
|
caretLeftGlyph.setFontSize(15);
|
||||||
|
return caretLeftGlyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Glyph getRightGlyph() {
|
||||||
|
Glyph caretRightGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.CARET_RIGHT);
|
||||||
|
caretRightGlyph.getStyleClass().add("label-right-icon");
|
||||||
|
caretRightGlyph.setFontSize(15);
|
||||||
|
return caretRightGlyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record OutputLabel(HBox hBox, double width, String text) {}
|
||||||
|
}
|
|
@ -25,6 +25,8 @@ public class FontAwesome5 extends GlyphFont {
|
||||||
BTC('\uf15a'),
|
BTC('\uf15a'),
|
||||||
BULLSEYE('\uf140'),
|
BULLSEYE('\uf140'),
|
||||||
CAMERA('\uf030'),
|
CAMERA('\uf030'),
|
||||||
|
CARET_LEFT('\uf0d9'),
|
||||||
|
CARET_RIGHT('\uf0da'),
|
||||||
CHECK_CIRCLE('\uf058'),
|
CHECK_CIRCLE('\uf058'),
|
||||||
CIRCLE('\uf111'),
|
CIRCLE('\uf111'),
|
||||||
COINS('\uf51e'),
|
COINS('\uf51e'),
|
||||||
|
|
|
@ -95,6 +95,9 @@ public class HeadersController extends TransactionFormController implements Init
|
||||||
@FXML
|
@FXML
|
||||||
private TransactionDiagram transactionDiagram;
|
private TransactionDiagram transactionDiagram;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TransactionDiagramLabel transactionDiagramLabel;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private IntegerSpinner version;
|
private IntegerSpinner version;
|
||||||
|
|
||||||
|
@ -440,6 +443,7 @@ public class HeadersController extends TransactionFormController implements Init
|
||||||
updateFee(feeAmt);
|
updateFee(feeAmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transactionDiagram.labelProperty().set(transactionDiagramLabel);
|
||||||
transactionDiagram.update(getWalletTransaction(headersForm.getInputTransactions()));
|
transactionDiagram.update(getWalletTransaction(headersForm.getInputTransactions()));
|
||||||
|
|
||||||
blockchainForm.managedProperty().bind(blockchainForm.visibleProperty());
|
blockchainForm.managedProperty().bind(blockchainForm.visibleProperty());
|
||||||
|
@ -627,9 +631,13 @@ public class HeadersController extends TransactionFormController implements Init
|
||||||
} else {
|
} else {
|
||||||
payments.add(new Payment(txOutput.getScript().getToAddress(), ".." + changeNode + " (Mix)", txOutput.getValue(), false, Payment.Type.MIX));
|
payments.add(new Payment(txOutput.getScript().getToAddress(), ".." + changeNode + " (Mix)", txOutput.getValue(), false, Payment.Type.MIX));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if(changeMap.containsKey(changeNode)) {
|
||||||
|
payments.add(new Payment(txOutput.getScript().getToAddress(), headersForm.getName(), txOutput.getValue(), false, Payment.Type.DEFAULT));
|
||||||
} else {
|
} else {
|
||||||
changeMap.put(changeNode, txOutput.getValue());
|
changeMap.put(changeNode, txOutput.getValue());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Payment.Type paymentType = Payment.Type.DEFAULT;
|
Payment.Type paymentType = Payment.Type.DEFAULT;
|
||||||
Wallet masterWallet = wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
|
Wallet masterWallet = wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
|
||||||
|
|
|
@ -70,14 +70,14 @@ public class TransactionController implements Initializable {
|
||||||
public void initializeView() {
|
public void initializeView() {
|
||||||
fetchTransactions();
|
fetchTransactions();
|
||||||
initializeTxTree();
|
initializeTxTree();
|
||||||
transactionMasterDetail.setDividerPosition(0.82);
|
transactionMasterDetail.setDividerPosition(0.85);
|
||||||
transactionMasterDetail.setShowDetailNode(Config.get().isShowTransactionHex());
|
transactionMasterDetail.setShowDetailNode(Config.get().isShowTransactionHex());
|
||||||
txhex.setTransaction(getTransaction());
|
txhex.setTransaction(getTransaction());
|
||||||
highlightTxHex();
|
highlightTxHex();
|
||||||
|
|
||||||
transactionMasterDetail.sceneProperty().addListener((observable, oldScene, newScene) -> {
|
transactionMasterDetail.sceneProperty().addListener((observable, oldScene, newScene) -> {
|
||||||
if(oldScene == null && newScene != null) {
|
if(oldScene == null && newScene != null) {
|
||||||
transactionMasterDetail.setDividerPosition(AppServices.isReducedWindowHeight(transactionMasterDetail) ? 0.9 : 0.82);
|
transactionMasterDetail.setDividerPosition(AppServices.isReducedWindowHeight(transactionMasterDetail) ? 0.9 : 0.85);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,22 @@
|
||||||
-fx-text-fill: rgb(238, 210, 2);
|
-fx-text-fill: rgb(238, 210, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#transactionDiagramLabel .button {
|
||||||
|
-fx-padding: 0;
|
||||||
|
-fx-pref-height: 18;
|
||||||
|
-fx-pref-width: 18;
|
||||||
|
-fx-border-width: 0;
|
||||||
|
-fx-background-color: -fx-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
#transactionDiagramLabel .button .glyph-font {
|
||||||
|
-fx-text-fill: #0184bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#transactionDiagramLabel .button:hover .glyph-font {
|
||||||
|
-fx-text-fill: #259cf5;
|
||||||
|
}
|
||||||
|
|
||||||
.details-lower .fieldset {
|
.details-lower .fieldset {
|
||||||
-fx-padding: 0 0 0 0;
|
-fx-padding: 0 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
<?import javafx.scene.control.Tab?>
|
<?import javafx.scene.control.Tab?>
|
||||||
<?import com.sparrowwallet.sparrow.control.TransactionDiagram?>
|
<?import com.sparrowwallet.sparrow.control.TransactionDiagram?>
|
||||||
<?import com.sparrowwallet.sparrow.control.IntegerSpinner?>
|
<?import com.sparrowwallet.sparrow.control.IntegerSpinner?>
|
||||||
|
<?import com.sparrowwallet.sparrow.control.TransactionDiagramLabel?>
|
||||||
|
|
||||||
<GridPane hgap="10.0" vgap="10.0" styleClass="tx-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.transaction.HeadersController" stylesheets="@headers.css, @transaction.css, @../general.css">
|
<GridPane hgap="10.0" vgap="10.0" styleClass="tx-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.transaction.HeadersController" stylesheets="@headers.css, @transaction.css, @../general.css">
|
||||||
<padding>
|
<padding>
|
||||||
|
@ -73,7 +74,10 @@
|
||||||
|
|
||||||
<TabPane side="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2" styleClass="headers-tabs">
|
<TabPane side="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2" styleClass="headers-tabs">
|
||||||
<Tab text="Overview" closable="false">
|
<Tab text="Overview" closable="false">
|
||||||
|
<VBox spacing="8">
|
||||||
<TransactionDiagram fx:id="transactionDiagram" maxWidth="700" final="true"/>
|
<TransactionDiagram fx:id="transactionDiagram" maxWidth="700" final="true"/>
|
||||||
|
<TransactionDiagramLabel fx:id="transactionDiagramLabel" maxWidth="640" prefWidth="640" />
|
||||||
|
</VBox>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab text="Detail" closable="false">
|
<Tab text="Detail" closable="false">
|
||||||
<GridPane hgap="10.0" vgap="10.0">
|
<GridPane hgap="10.0" vgap="10.0">
|
||||||
|
|
BIN
src/main/resources/font/RobotoMono-Italic.ttf
Normal file
BIN
src/main/resources/font/RobotoMono-Italic.ttf
Normal file
Binary file not shown.
Loading…
Reference in a new issue