mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
save transaction diagram as image through context menu on transaction label
This commit is contained in:
parent
923c61fceb
commit
da3399468c
3 changed files with 85 additions and 19 deletions
|
@ -21,6 +21,7 @@ import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
@ -28,6 +29,10 @@ import javafx.scene.Group;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.*;
|
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.Clipboard;
|
||||||
import javafx.scene.input.ClipboardContent;
|
import javafx.scene.input.ClipboardContent;
|
||||||
import javafx.scene.input.MouseButton;
|
import javafx.scene.input.MouseButton;
|
||||||
|
@ -37,6 +42,7 @@ import javafx.scene.paint.Color;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
import javafx.scene.shape.CubicCurve;
|
import javafx.scene.shape.CubicCurve;
|
||||||
import javafx.scene.shape.Line;
|
import javafx.scene.shape.Line;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.StageStyle;
|
import javafx.stage.StageStyle;
|
||||||
|
@ -45,7 +51,12 @@ import org.controlsfx.glyphfont.FontAwesome;
|
||||||
import org.controlsfx.glyphfont.Glyph;
|
import org.controlsfx.glyphfont.Glyph;
|
||||||
import org.controlsfx.tools.Platform;
|
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.*;
|
||||||
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.sparrowwallet.sparrow.control.CoinLabel.BTC_FORMAT;
|
import static com.sparrowwallet.sparrow.control.CoinLabel.BTC_FORMAT;
|
||||||
|
@ -75,7 +86,7 @@ public class TransactionDiagram extends GridPane {
|
||||||
public void handle(MouseEvent event) {
|
public void handle(MouseEvent event) {
|
||||||
if(!event.isConsumed() && event.getButton() != MouseButton.SECONDARY) {
|
if(!event.isConsumed() && event.getButton() != MouseButton.SECONDARY) {
|
||||||
Stage stage = new Stage(StageStyle.UNDECORATED);
|
Stage stage = new Stage(StageStyle.UNDECORATED);
|
||||||
stage.setTitle(walletTx.getPayments().iterator().next().getLabel());
|
stage.setTitle(getDiagramTitle());
|
||||||
stage.initOwner(TransactionDiagram.this.getScene().getWindow());
|
stage.initOwner(TransactionDiagram.this.getScene().getWindow());
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
|
@ -98,7 +109,8 @@ public class TransactionDiagram extends GridPane {
|
||||||
expandedDiagram = new TransactionDiagram();
|
expandedDiagram = new TransactionDiagram();
|
||||||
expandedDiagram.setId("transactionDiagram");
|
expandedDiagram.setId("transactionDiagram");
|
||||||
expandedDiagram.setExpanded(true);
|
expandedDiagram.setExpanded(true);
|
||||||
updateExpandedDiagram();
|
expandedDiagram.setFinal(isFinal());
|
||||||
|
updateDerivedDiagram(expandedDiagram);
|
||||||
|
|
||||||
HBox buttonBox = new HBox();
|
HBox buttonBox = new HBox();
|
||||||
buttonBox.setAlignment(Pos.CENTER_RIGHT);
|
buttonBox.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
@ -136,7 +148,7 @@ public class TransactionDiagram extends GridPane {
|
||||||
update();
|
update();
|
||||||
setOnMouseClicked(expandedDiagramHandler);
|
setOnMouseClicked(expandedDiagramHandler);
|
||||||
if(expandedDiagram != null) {
|
if(expandedDiagram != null) {
|
||||||
updateExpandedDiagram();
|
updateDerivedDiagram(expandedDiagram);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,20 +177,22 @@ public class TransactionDiagram extends GridPane {
|
||||||
getChildren().clear();
|
getChildren().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateExpandedDiagram() {
|
private void updateDerivedDiagram(TransactionDiagram diagram) {
|
||||||
expandedDiagram.setFinal(isFinal());
|
diagram.setOptimizationStrategy(getOptimizationStrategy());
|
||||||
expandedDiagram.setOptimizationStrategy(getOptimizationStrategy());
|
diagram.walletTx = walletTx;
|
||||||
expandedDiagram.walletTx = walletTx;
|
|
||||||
|
|
||||||
List<Map<BlockTransactionHashIndex, WalletNode>> utxoSets = expandedDiagram.getDisplayedUtxoSets();
|
if(diagram.isExpanded()) {
|
||||||
int maxSetSize = utxoSets.stream().mapToInt(Map::size).max().orElse(0);
|
List<Map<BlockTransactionHashIndex, WalletNode>> utxoSets = diagram.getDisplayedUtxoSets();
|
||||||
int maxRows = Math.max(maxSetSize * utxoSets.size(), walletTx.getPayments().size() + 2);
|
int maxSetSize = utxoSets.stream().mapToInt(Map::size).max().orElse(0);
|
||||||
double diagramHeight = Math.max(DIAGRAM_HEIGHT, Math.min(EXPANDED_DIAGRAM_HEIGHT, maxRows * ROW_HEIGHT));
|
int maxRows = Math.max(maxSetSize * utxoSets.size(), walletTx.getPayments().size() + 2);
|
||||||
expandedDiagram.setMinHeight(diagramHeight);
|
double diagramHeight = Math.max(DIAGRAM_HEIGHT, Math.min(EXPANDED_DIAGRAM_HEIGHT, maxRows * ROW_HEIGHT));
|
||||||
expandedDiagram.setMaxHeight(diagramHeight);
|
diagram.setMinHeight(diagramHeight);
|
||||||
expandedDiagram.update();
|
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();
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -797,12 +811,59 @@ public class TransactionDiagram extends GridPane {
|
||||||
tooltip.setShowDuration(Duration.INDEFINITE);
|
tooltip.setShowDuration(Duration.INDEFINITE);
|
||||||
tooltip.getStyleClass().add("transaction-tooltip");
|
tooltip.getStyleClass().add("transaction-tooltip");
|
||||||
txLabel.setTooltip(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(txLabel);
|
||||||
txPane.getChildren().add(createSpacer());
|
txPane.getChildren().add(createSpacer());
|
||||||
|
|
||||||
return txPane;
|
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() {
|
public double getDiagramHeight() {
|
||||||
if(isExpanded()) {
|
if(isExpanded()) {
|
||||||
return getMaxHeight();
|
return getMaxHeight();
|
||||||
|
|
|
@ -206,7 +206,7 @@
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root #transactionDiagram .coins-icon, #transactionDiagram .user-icon {
|
.root #transactionDiagram .coins-icon, .root #transactionDiagram .user-icon {
|
||||||
-fx-text-fill: lightgray;
|
-fx-text-fill: lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +214,11 @@
|
||||||
-fx-fill: lightgray;
|
-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 {
|
.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%);
|
-fx-background-color: -fx-box-border, radial-gradient(center 50% 50%, radius 50%, #e06c75 70%, derive(-fx-control-inner-background, -9%) 100%);
|
||||||
}
|
}
|
|
@ -80,13 +80,13 @@
|
||||||
|
|
||||||
#transactionDiagram .inputs-type, #transactionDiagram .input-line, #transactionDiagram .output-line {
|
#transactionDiagram .inputs-type, #transactionDiagram .input-line, #transactionDiagram .output-line {
|
||||||
-fx-fill: transparent;
|
-fx-fill: transparent;
|
||||||
-fx-text-fill: #696c77;
|
-fx-text-fill: -fx-text-background-color;
|
||||||
-fx-stroke: #696c77;
|
-fx-stroke: -fx-text-background-color;
|
||||||
-fx-stroke-width: 1px;
|
-fx-stroke-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#transactionDiagram .size-indicator {
|
#transactionDiagram .size-indicator {
|
||||||
-fx-fill: -fx-text-base-color;
|
-fx-fill: -fx-text-background-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
#transactionDiagram .input-dashed-line {
|
#transactionDiagram .input-dashed-line {
|
||||||
|
|
Loading…
Reference in a new issue