diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java index 77043271..1dea050a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java @@ -21,6 +21,7 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; +import javafx.embed.swing.SwingFXUtils; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -28,6 +29,10 @@ import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.MenuItem; +import javafx.scene.image.Image; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.scene.input.MouseButton; @@ -37,6 +42,7 @@ import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.CubicCurve; import javafx.scene.shape.Line; +import javafx.stage.FileChooser; import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; @@ -45,7 +51,12 @@ import org.controlsfx.glyphfont.FontAwesome; import org.controlsfx.glyphfont.Glyph; import org.controlsfx.tools.Platform; +import javax.imageio.ImageIO; +import java.awt.image.*; +import java.io.File; +import java.io.IOException; import java.util.*; +import java.util.List; import java.util.stream.Collectors; import static com.sparrowwallet.sparrow.control.CoinLabel.BTC_FORMAT; @@ -75,7 +86,7 @@ public class TransactionDiagram extends GridPane { public void handle(MouseEvent event) { if(!event.isConsumed() && event.getButton() != MouseButton.SECONDARY) { Stage stage = new Stage(StageStyle.UNDECORATED); - stage.setTitle(walletTx.getPayments().iterator().next().getLabel()); + stage.setTitle(getDiagramTitle()); stage.initOwner(TransactionDiagram.this.getScene().getWindow()); stage.initModality(Modality.WINDOW_MODAL); stage.setResizable(false); @@ -98,7 +109,8 @@ public class TransactionDiagram extends GridPane { expandedDiagram = new TransactionDiagram(); expandedDiagram.setId("transactionDiagram"); expandedDiagram.setExpanded(true); - updateExpandedDiagram(); + expandedDiagram.setFinal(isFinal()); + updateDerivedDiagram(expandedDiagram); HBox buttonBox = new HBox(); buttonBox.setAlignment(Pos.CENTER_RIGHT); @@ -136,7 +148,7 @@ public class TransactionDiagram extends GridPane { update(); setOnMouseClicked(expandedDiagramHandler); if(expandedDiagram != null) { - updateExpandedDiagram(); + updateDerivedDiagram(expandedDiagram); } } } @@ -165,20 +177,22 @@ public class TransactionDiagram extends GridPane { getChildren().clear(); } - private void updateExpandedDiagram() { - expandedDiagram.setFinal(isFinal()); - expandedDiagram.setOptimizationStrategy(getOptimizationStrategy()); - expandedDiagram.walletTx = walletTx; + private void updateDerivedDiagram(TransactionDiagram diagram) { + diagram.setOptimizationStrategy(getOptimizationStrategy()); + diagram.walletTx = walletTx; - List> utxoSets = expandedDiagram.getDisplayedUtxoSets(); - int maxSetSize = utxoSets.stream().mapToInt(Map::size).max().orElse(0); - int maxRows = Math.max(maxSetSize * utxoSets.size(), walletTx.getPayments().size() + 2); - double diagramHeight = Math.max(DIAGRAM_HEIGHT, Math.min(EXPANDED_DIAGRAM_HEIGHT, maxRows * ROW_HEIGHT)); - expandedDiagram.setMinHeight(diagramHeight); - expandedDiagram.setMaxHeight(diagramHeight); - expandedDiagram.update(); + if(diagram.isExpanded()) { + List> utxoSets = diagram.getDisplayedUtxoSets(); + int maxSetSize = utxoSets.stream().mapToInt(Map::size).max().orElse(0); + int maxRows = Math.max(maxSetSize * utxoSets.size(), walletTx.getPayments().size() + 2); + double diagramHeight = Math.max(DIAGRAM_HEIGHT, Math.min(EXPANDED_DIAGRAM_HEIGHT, maxRows * ROW_HEIGHT)); + diagram.setMinHeight(diagramHeight); + diagram.setMaxHeight(diagramHeight); + } - if(expandedDiagram.getScene() != null && expandedDiagram.getScene().getWindow() instanceof Stage stage) { + diagram.update(); + + if(diagram.getScene() != null && diagram.getScene().getWindow() instanceof Stage stage) { stage.sizeToScene(); } } @@ -797,12 +811,59 @@ public class TransactionDiagram extends GridPane { tooltip.setShowDuration(Duration.INDEFINITE); tooltip.getStyleClass().add("transaction-tooltip"); txLabel.setTooltip(tooltip); + ContextMenu contextMenu = new ContextMenu(); + MenuItem menuItem = new MenuItem("Save as Image..."); + menuItem.setOnAction(event -> { + contextMenu.hide(); + saveAsImage(); + }); + contextMenu.getItems().add(menuItem); + txLabel.setContextMenu(contextMenu); + txPane.getChildren().add(txLabel); txPane.getChildren().add(createSpacer()); return txPane; } + private void saveAsImage() { + Stage window = new Stage(); + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save Image"); + fileChooser.setInitialFileName(getDiagramTitle() + ".png"); + AppServices.moveToActiveWindowScreen(window, 800, 450); + File file = fileChooser.showSaveDialog(window); + if(file != null) { + TransactionDiagram transactionDiagram = new TransactionDiagram(); + transactionDiagram.setId("transactionDiagram"); + transactionDiagram.setFinal(true); + transactionDiagram.setExpanded(isExpanded()); + transactionDiagram.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, null, null))); + transactionDiagram.setStyle("-fx-text-background-color: #000000"); + updateDerivedDiagram(transactionDiagram); + Scene scene = new Scene(transactionDiagram); + scene.setFill(Color.TRANSPARENT); + scene.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm()); + scene.getStylesheets().add(AppServices.class.getResource("wallet/wallet.css").toExternalForm()); + scene.getStylesheets().add(AppServices.class.getResource("wallet/send.css").toExternalForm()); + Image image = scene.snapshot(null); + BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null); + try { + ImageIO.write(bufferedImage, "png", file); + } catch(IOException e) { + AppServices.showErrorDialog("Error saving image", e.getMessage()); + } + } + } + + private String getDiagramTitle() { + if(!isFinal() && walletTx.getPayments().size() > 0 && walletTx.getPayments().get(0).getLabel() != null) { + return walletTx.getPayments().get(0).getLabel(); + } else { + return "[" + walletTx.getTransaction().getTxId().toString().substring(0, 6) + "]"; + } + } + public double getDiagramHeight() { if(isExpanded()) { return getMaxHeight(); diff --git a/src/main/resources/com/sparrowwallet/sparrow/darktheme.css b/src/main/resources/com/sparrowwallet/sparrow/darktheme.css index 392ba0a7..6d5641f6 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/darktheme.css +++ b/src/main/resources/com/sparrowwallet/sparrow/darktheme.css @@ -206,7 +206,7 @@ -fx-background-color: transparent; } -.root #transactionDiagram .coins-icon, #transactionDiagram .user-icon { +.root #transactionDiagram .coins-icon, .root #transactionDiagram .user-icon { -fx-text-fill: lightgray; } @@ -214,6 +214,11 @@ -fx-fill: lightgray; } +.root #transactionDiagram .inputs-type, .root #transactionDiagram .input-line, .root #transactionDiagram .output-line { + -fx-text-fill: #696c77; + -fx-stroke: #696c77; +} + .root .progress-indicator.progress-timer.warn > .determinate-indicator > .indicator { -fx-background-color: -fx-box-border, radial-gradient(center 50% 50%, radius 50%, #e06c75 70%, derive(-fx-control-inner-background, -9%) 100%); } \ No newline at end of file diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css b/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css index 3c3e8280..60e8df40 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css @@ -80,13 +80,13 @@ #transactionDiagram .inputs-type, #transactionDiagram .input-line, #transactionDiagram .output-line { -fx-fill: transparent; - -fx-text-fill: #696c77; - -fx-stroke: #696c77; + -fx-text-fill: -fx-text-background-color; + -fx-stroke: -fx-text-background-color; -fx-stroke-width: 1px; } #transactionDiagram .size-indicator { - -fx-fill: -fx-text-base-color; + -fx-fill: -fx-text-background-color; } #transactionDiagram .input-dashed-line {