update utxo tab ui to show utxo balance and count next to smaller utxo chart

This commit is contained in:
Craig Raw 2021-12-07 13:29:03 +02:00
parent 1b61a78e6d
commit 3c94664ac3
8 changed files with 112 additions and 66 deletions

View file

@ -144,10 +144,6 @@ public class AppController implements Initializable {
private CheckMenuItem showLoadingLog; private CheckMenuItem showLoadingLog;
private static final BooleanProperty showLoadingLogProperty = new SimpleBooleanProperty(); private static final BooleanProperty showLoadingLogProperty = new SimpleBooleanProperty();
@FXML
private CheckMenuItem showUtxosChart;
private static final BooleanProperty showUtxosChartProperty = new SimpleBooleanProperty();
@FXML @FXML
private CheckMenuItem showTxHex; private CheckMenuItem showTxHex;
private static final BooleanProperty showTxHexProperty = new SimpleBooleanProperty(); private static final BooleanProperty showTxHexProperty = new SimpleBooleanProperty();
@ -320,8 +316,6 @@ public class AppController implements Initializable {
showTxHex.selectedProperty().bindBidirectional(showTxHexProperty); showTxHex.selectedProperty().bindBidirectional(showTxHexProperty);
showLoadingLogProperty.set(Config.get().isShowLoadingLog()); showLoadingLogProperty.set(Config.get().isShowLoadingLog());
showLoadingLog.selectedProperty().bindBidirectional(showLoadingLogProperty); showLoadingLog.selectedProperty().bindBidirectional(showLoadingLogProperty);
showUtxosChartProperty.set(Config.get().isShowUtxosChart());
showUtxosChart.selectedProperty().bindBidirectional(showUtxosChartProperty);
preventSleepProperty.set(Config.get().isPreventSleep()); preventSleepProperty.set(Config.get().isPreventSleep());
preventSleep.selectedProperty().bindBidirectional(preventSleepProperty); preventSleep.selectedProperty().bindBidirectional(preventSleepProperty);
@ -802,12 +796,6 @@ public class AppController implements Initializable {
EventManager.get().post(new LoadingLogChangedEvent(item.isSelected())); EventManager.get().post(new LoadingLogChangedEvent(item.isSelected()));
} }
public void showUtxosChart(ActionEvent event) {
CheckMenuItem item = (CheckMenuItem)event.getSource();
Config.get().setShowUtxosChart(item.isSelected());
EventManager.get().post(new UtxosChartChangedEvent(item.isSelected()));
}
public void showTxHex(ActionEvent event) { public void showTxHex(ActionEvent event) {
CheckMenuItem item = (CheckMenuItem)event.getSource(); CheckMenuItem item = (CheckMenuItem)event.getSource();
Config.get().setShowTransactionHex(item.isSelected()); Config.get().setShowTransactionHex(item.isSelected());
@ -1869,7 +1857,6 @@ public class AppController implements Initializable {
lockWallet.setDisable(true); lockWallet.setDisable(true);
exportWallet.setDisable(true); exportWallet.setDisable(true);
showLoadingLog.setDisable(true); showLoadingLog.setDisable(true);
showUtxosChart.setDisable(true);
showTxHex.setDisable(false); showTxHex.setDisable(false);
findMixingPartner.setDisable(true); findMixingPartner.setDisable(true);
} else if(event instanceof WalletTabSelectedEvent) { } else if(event instanceof WalletTabSelectedEvent) {
@ -1880,7 +1867,6 @@ public class AppController implements Initializable {
lockWallet.setDisable(walletTabData.getWalletForm().lockedProperty().get()); lockWallet.setDisable(walletTabData.getWalletForm().lockedProperty().get());
exportWallet.setDisable(walletTabData.getWallet() == null || !walletTabData.getWallet().isValid()); exportWallet.setDisable(walletTabData.getWallet() == null || !walletTabData.getWallet().isValid());
showLoadingLog.setDisable(false); showLoadingLog.setDisable(false);
showUtxosChart.setDisable(false);
showTxHex.setDisable(true); showTxHex.setDisable(true);
findMixingPartner.setDisable(exportWallet.isDisable() || !SorobanServices.canWalletMix(walletTabData.getWallet()) || !AppServices.onlineProperty().get()); findMixingPartner.setDisable(exportWallet.isDisable() || !SorobanServices.canWalletMix(walletTabData.getWallet()) || !AppServices.onlineProperty().get());
} }

View file

@ -9,14 +9,18 @@ import com.sparrowwallet.sparrow.wallet.WalletUtxosEntry;
import javafx.beans.NamedArg; import javafx.beans.NamedArg;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.chart.*; import javafx.scene.chart.*;
import javafx.scene.control.Tooltip;
import javafx.util.Duration;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class UtxosChart extends BarChart<String, Number> { public class UtxosChart extends BarChart<String, Number> {
private static final int MAX_BARS = 8; private static final int MAX_BARS = 8;
private static final String OTHER_CATEGORY = "Other"; private static final String OTHER_CATEGORY = "Other";
private static final int TOOLTIP_SHOW_DELAY = 50;
private List<Entry> selectedEntries; private List<Entry> selectedEntries;
private int totalUtxos; private int totalUtxos;
@ -57,9 +61,11 @@ public class UtxosChart extends BarChart<String, Number> {
XYChart.Data<String, Number> existingData = utxoSeries.getData().get(i); XYChart.Data<String, Number> existingData = utxoSeries.getData().get(i);
if(!newData.getXValue().equals(existingData.getXValue()) || !newData.getYValue().equals(existingData.getYValue()) || (newData.getExtraValue() instanceof Entry && !newData.getExtraValue().equals(existingData.getExtraValue()))) { if(!newData.getXValue().equals(existingData.getXValue()) || !newData.getYValue().equals(existingData.getYValue()) || (newData.getExtraValue() instanceof Entry && !newData.getExtraValue().equals(existingData.getExtraValue()))) {
utxoSeries.getData().set(i, newData); utxoSeries.getData().set(i, newData);
installTooltip(newData);
} }
} else { } else {
utxoSeries.getData().add(newData); utxoSeries.getData().add(newData);
installTooltip(newData);
} }
} }
@ -74,12 +80,21 @@ public class UtxosChart extends BarChart<String, Number> {
private String getCategoryName(Entry entry) { private String getCategoryName(Entry entry) {
if(entry.getLabel() != null && !entry.getLabel().isEmpty()) { if(entry.getLabel() != null && !entry.getLabel().isEmpty()) {
return entry.getLabel().length() > 15 ? entry.getLabel().substring(0, 15) + "..." + "\n" + ((UtxoEntry)entry).getDescription() : entry.getLabel() + "\n" + ((UtxoEntry)entry).getDescription(); return entry.getLabel() + "\n" + ((UtxoEntry)entry).getDescription();
} }
return ((UtxoEntry)entry).getDescription(); return ((UtxoEntry)entry).getDescription();
} }
private void installTooltip(XYChart.Data<String, Number> item) {
Tooltip.uninstall(item.getNode(), null);
String satsValue = String.format(Locale.ENGLISH, "%,d", item.getYValue());
Tooltip tooltip = new Tooltip(item.getXValue() + "\n" + satsValue + " sats");
tooltip.setShowDelay(Duration.millis(TOOLTIP_SHOW_DELAY));
Tooltip.install(item.getNode(), tooltip);
}
public void select(List<Entry> entries) { public void select(List<Entry> entries) {
Set<Node> selectedBars = lookupAll(".chart-bar.selected"); Set<Node> selectedBars = lookupAll(".chart-bar.selected");
for(Node selectedBar : selectedBars) { for(Node selectedBar : selectedBars) {

View file

@ -40,7 +40,6 @@ public class Config {
private boolean hideEmptyUsedAddresses = false; private boolean hideEmptyUsedAddresses = false;
private boolean showTransactionHex = true; private boolean showTransactionHex = true;
private boolean showLoadingLog = true; private boolean showLoadingLog = true;
private boolean showUtxosChart = true;
private boolean preventSleep = false; private boolean preventSleep = false;
private List<File> recentWalletFiles; private List<File> recentWalletFiles;
private Integer keyDerivationPeriod; private Integer keyDerivationPeriod;
@ -273,15 +272,6 @@ public class Config {
flush(); flush();
} }
public boolean isShowUtxosChart() {
return showUtxosChart;
}
public void setShowUtxosChart(boolean showUtxosChart) {
this.showUtxosChart = showUtxosChart;
flush();
}
public boolean isPreventSleep() { public boolean isPreventSleep() {
return preventSleep; return preventSleep;
} }
@ -388,7 +378,9 @@ public class Config {
public void changePublicServer() { public void changePublicServer() {
List<String> otherServers = PublicElectrumServer.getServers().stream().map(PublicElectrumServer::getUrl).filter(url -> !url.equals(getPublicElectrumServer())).collect(Collectors.toList()); List<String> otherServers = PublicElectrumServer.getServers().stream().map(PublicElectrumServer::getUrl).filter(url -> !url.equals(getPublicElectrumServer())).collect(Collectors.toList());
setPublicElectrumServer(otherServers.get(new Random().nextInt(otherServers.size()))); if(!otherServers.isEmpty()) {
setPublicElectrumServer(otherServers.get(new Random().nextInt(otherServers.size())));
}
} }
public String getCoreServer() { public String getCoreServer() {

View file

@ -106,15 +106,6 @@ public class TransactionsController extends WalletFormController implements Init
loadingLog.setEditable(false); loadingLog.setEditable(false);
} }
private void setFiatBalance(FiatLabel fiatLabel, CurrencyRate currencyRate, long balance) {
if(currencyRate != null && currencyRate.isAvailable() && balance > 0) {
fiatLabel.set(currencyRate, balance);
} else {
fiatLabel.setCurrency(null);
fiatLabel.setBtcRate(0.0);
}
}
private void setTransactionCount(WalletTransactionsEntry walletTransactionsEntry) { private void setTransactionCount(WalletTransactionsEntry walletTransactionsEntry) {
transactionCount.setText(walletTransactionsEntry.getChildren() != null ? Integer.toString(walletTransactionsEntry.getChildren().size()) : "0"); transactionCount.setText(walletTransactionsEntry.getChildren() != null ? Integer.toString(walletTransactionsEntry.getChildren().size()) : "0");
} }

View file

@ -52,6 +52,21 @@ import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
public class UtxosController extends WalletFormController implements Initializable { public class UtxosController extends WalletFormController implements Initializable {
private static final Logger log = LoggerFactory.getLogger(UtxosController.class); private static final Logger log = LoggerFactory.getLogger(UtxosController.class);
@FXML
private CoinLabel balance;
@FXML
private FiatLabel fiatBalance;
@FXML
private CoinLabel mempoolBalance;
@FXML
private FiatLabel fiatMempoolBalance;
@FXML
private CopyableLabel utxoCount;
@FXML @FXML
private UtxosTreeTable utxosTable; private UtxosTreeTable utxosTable;
@ -115,8 +130,17 @@ public class UtxosController extends WalletFormController implements Initializab
@Override @Override
public void initializeView() { public void initializeView() {
utxosTable.initialize(getWalletForm().getWalletUtxosEntry()); balance.valueProperty().addListener((observable, oldValue, newValue) -> {
utxosChart.initialize(getWalletForm().getWalletUtxosEntry()); setFiatBalance(fiatBalance, AppServices.getFiatCurrencyExchangeRate(), newValue.longValue());
});
mempoolBalance.valueProperty().addListener((observable, oldValue, newValue) -> {
setFiatBalance(fiatMempoolBalance, AppServices.getFiatCurrencyExchangeRate(), newValue.longValue());
});
WalletUtxosEntry walletUtxosEntry = getWalletForm().getWalletUtxosEntry();
updateFields(walletUtxosEntry);
utxosTable.initialize(walletUtxosEntry);
utxosChart.initialize(walletUtxosEntry);
mixButtonsBox.managedProperty().bind(mixButtonsBox.visibleProperty()); mixButtonsBox.managedProperty().bind(mixButtonsBox.visibleProperty());
mixButtonsBox.setVisible(getWalletForm().getWallet().isWhirlpoolMixWallet()); mixButtonsBox.setVisible(getWalletForm().getWallet().isWhirlpoolMixWallet());
@ -159,9 +183,12 @@ public class UtxosController extends WalletFormController implements Initializab
utxosChart.select(selectedEntries); utxosChart.select(selectedEntries);
updateButtons(Config.get().getBitcoinUnit()); updateButtons(Config.get().getBitcoinUnit());
}); });
}
utxosChart.managedProperty().bind(utxosChart.visibleProperty()); private void updateFields(WalletUtxosEntry walletUtxosEntry) {
utxosChart.setVisible(Config.get().isShowUtxosChart() && !getWalletForm().getWallet().isWhirlpoolMixWallet()); balance.setValue(walletUtxosEntry.getChildren().stream().mapToLong(Entry::getValue).sum());
mempoolBalance.setValue(walletUtxosEntry.getChildren().stream().filter(entry -> ((UtxoEntry)entry).getHashIndex().getHeight() <= 0).mapToLong(Entry::getValue).sum());
utxoCount.setText(walletUtxosEntry.getChildren() != null ? Integer.toString(walletUtxosEntry.getChildren().size()) : "0");
} }
private boolean canWalletMix() { private boolean canWalletMix() {
@ -458,6 +485,7 @@ public class UtxosController extends WalletFormController implements Initializab
public void walletNodesChanged(WalletNodesChangedEvent event) { public void walletNodesChanged(WalletNodesChangedEvent event) {
if(event.getWallet().equals(walletForm.getWallet())) { if(event.getWallet().equals(walletForm.getWallet())) {
WalletUtxosEntry walletUtxosEntry = getWalletForm().getWalletUtxosEntry(); WalletUtxosEntry walletUtxosEntry = getWalletForm().getWalletUtxosEntry();
updateFields(walletUtxosEntry);
utxosTable.updateAll(walletUtxosEntry); utxosTable.updateAll(walletUtxosEntry);
utxosChart.update(walletUtxosEntry); utxosChart.update(walletUtxosEntry);
mixSelected.setVisible(canWalletMix()); mixSelected.setVisible(canWalletMix());
@ -478,6 +506,7 @@ public class UtxosController extends WalletFormController implements Initializab
utxosTable.getSelectionModel().clearSelection(); utxosTable.getSelectionModel().clearSelection();
} }
updateFields(walletUtxosEntry);
utxosTable.updateHistory(event.getHistoryChangedNodes()); utxosTable.updateHistory(event.getHistoryChangedNodes());
utxosChart.update(walletUtxosEntry); utxosChart.update(walletUtxosEntry);
} }
@ -497,6 +526,8 @@ public class UtxosController extends WalletFormController implements Initializab
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) { public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
utxosTable.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit()); utxosTable.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit());
utxosChart.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit()); utxosChart.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit());
balance.refresh(event.getBitcoinUnit());
mempoolBalance.refresh(event.getBitcoinUnit());
updateButtons(event.getBitcoinUnit()); updateButtons(event.getBitcoinUnit());
} }

View file

@ -5,8 +5,10 @@ import com.sparrowwallet.drongo.KeyDerivation;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.BaseController; import com.sparrowwallet.sparrow.BaseController;
import com.sparrowwallet.sparrow.CurrencyRate;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.WalletTabData; import com.sparrowwallet.sparrow.WalletTabData;
import com.sparrowwallet.sparrow.control.FiatLabel;
import com.sparrowwallet.sparrow.event.WalletTabsClosedEvent; import com.sparrowwallet.sparrow.event.WalletTabsClosedEvent;
public abstract class WalletFormController extends BaseController { public abstract class WalletFormController extends BaseController {
@ -53,4 +55,13 @@ public abstract class WalletFormController extends BaseController {
return node.getDerivationPath().replace("m", "multi"); return node.getDerivationPath().replace("m", "multi");
} }
protected void setFiatBalance(FiatLabel fiatLabel, CurrencyRate currencyRate, long balance) {
if(currencyRate != null && currencyRate.isAvailable() && balance > 0) {
fiatLabel.set(currencyRate, balance);
} else {
fiatLabel.setCurrency(null);
fiatLabel.setBtcRate(0.0);
}
}
} }

View file

@ -98,7 +98,6 @@
<CheckMenuItem fx:id="hideEmptyUsedAddresses" mnemonicParsing="false" text="Hide Empty Used Addresses" onAction="#hideEmptyUsedAddresses"/> <CheckMenuItem fx:id="hideEmptyUsedAddresses" mnemonicParsing="false" text="Hide Empty Used Addresses" onAction="#hideEmptyUsedAddresses"/>
<CheckMenuItem fx:id="useHdCameraResolution" mnemonicParsing="false" text="Use HD Camera Resolution" onAction="#useHdCameraResolution"/> <CheckMenuItem fx:id="useHdCameraResolution" mnemonicParsing="false" text="Use HD Camera Resolution" onAction="#useHdCameraResolution"/>
<CheckMenuItem fx:id="showLoadingLog" mnemonicParsing="false" text="Show Wallet Loading Log" onAction="#showLoadingLog" /> <CheckMenuItem fx:id="showLoadingLog" mnemonicParsing="false" text="Show Wallet Loading Log" onAction="#showLoadingLog" />
<CheckMenuItem fx:id="showUtxosChart" mnemonicParsing="false" text="Show UTXOs Chart" onAction="#showUtxosChart" />
<CheckMenuItem fx:id="showTxHex" mnemonicParsing="false" text="Show Transaction Hex" onAction="#showTxHex"/> <CheckMenuItem fx:id="showTxHex" mnemonicParsing="false" text="Show Transaction Hex" onAction="#showTxHex"/>
<SeparatorMenuItem /> <SeparatorMenuItem />
<MenuItem fx:id="minimizeToTray" mnemonicParsing="false" text="Minimize to System Tray" accelerator="Shortcut+Y" onAction="#minimizeToTray"/> <MenuItem fx:id="minimizeToTray" mnemonicParsing="false" text="Minimize to System Tray" accelerator="Shortcut+Y" onAction="#minimizeToTray"/>

View file

@ -5,35 +5,64 @@
<?import javafx.scene.*?> <?import javafx.scene.*?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.chart.BarChart?>
<?import com.sparrowwallet.sparrow.control.UtxosTreeTable?> <?import com.sparrowwallet.sparrow.control.UtxosTreeTable?>
<?import javafx.scene.chart.CategoryAxis?> <?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.NumberAxis?> <?import javafx.scene.chart.NumberAxis?>
<?import com.sparrowwallet.sparrow.control.UtxosChart?> <?import com.sparrowwallet.sparrow.control.UtxosChart?>
<?import org.controlsfx.glyphfont.Glyph?> <?import org.controlsfx.glyphfont.Glyph?>
<?import tornadofx.control.Form?>
<?import tornadofx.control.Fieldset?>
<?import tornadofx.control.Field?>
<?import com.sparrowwallet.sparrow.control.CoinLabel?>
<?import com.sparrowwallet.sparrow.control.FiatLabel?>
<?import com.sparrowwallet.sparrow.control.CopyableLabel?>
<BorderPane stylesheets="@utxos.css, @wallet.css, @../general.css" styleClass="wallet-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.wallet.UtxosController"> <BorderPane stylesheets="@utxos.css, @wallet.css, @../general.css" styleClass="wallet-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.wallet.UtxosController">
<padding>
<Insets left="25.0" right="25.0" top="15.0" bottom="25.0" />
</padding>
<center> <center>
<VBox spacing="10.0"> <VBox spacing="10.0">
<padding>
<Insets left="25.0" right="25.0" top="25.0" bottom="25.0" />
</padding>
<GridPane styleClass="send-form" hgap="10.0" vgap="10.0">
<columnConstraints>
<ColumnConstraints percentWidth="50" />
<ColumnConstraints percentWidth="50" />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<Form GridPane.columnIndex="0" GridPane.rowIndex="0">
<Fieldset inputGrow="SOMETIMES" text="Unspent Transaction Outputs" styleClass="header">
<Field text="Balance:">
<CoinLabel fx:id="balance"/><Region HBox.hgrow="ALWAYS"/><FiatLabel fx:id="fiatBalance" minWidth="110" />
</Field>
<Field text="Mempool:">
<CoinLabel fx:id="mempoolBalance" /><Region HBox.hgrow="ALWAYS"/><FiatLabel fx:id="fiatMempoolBalance" minWidth="110" />
</Field>
<Field text="UTXOs:">
<CopyableLabel fx:id="utxoCount" />
<Button fx:id="exportCsv" maxHeight="25" onAction="#exportUtxos" translateY="-1" styleClass="icon-button">
<graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" icon="ARROW_CIRCLE_DOWN" fontSize="12" />
</graphic>
<tooltip>
<Tooltip text="Export UTXOs as CSV" />
</tooltip>
</Button>
</Field>
</Fieldset>
</Form>
<UtxosChart fx:id="utxosChart" legendVisible="false" verticalGridLinesVisible="false" animated="false" GridPane.columnIndex="1" GridPane.rowIndex="0" maxHeight="140">
<xAxis>
<CategoryAxis side="BOTTOM" animated="false" tickLabelsVisible="false" tickMarkVisible="false" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</UtxosChart>
</GridPane>
<BorderPane VBox.vgrow="ALWAYS"> <BorderPane VBox.vgrow="ALWAYS">
<top>
<HBox alignment="CENTER_LEFT">
<Label styleClass="utxos-treetable-label" text="Unspent Transaction Outputs"/>
<Button onAction="#exportUtxos" styleClass="icon-button">
<graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" icon="ARROW_CIRCLE_DOWN" fontSize="12" />
</graphic>
<tooltip>
<Tooltip text="Export UTXOs as CSV" />
</tooltip>
</Button>
</HBox>
</top>
<center> <center>
<UtxosTreeTable fx:id="utxosTable" /> <UtxosTreeTable fx:id="utxosTable" />
</center> </center>
@ -69,14 +98,6 @@
</HBox> </HBox>
</bottom> </bottom>
</BorderPane> </BorderPane>
<UtxosChart fx:id="utxosChart" legendVisible="false" verticalGridLinesVisible="false" animated="false" prefHeight="250">
<xAxis>
<CategoryAxis side="BOTTOM" animated="false" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</UtxosChart>
</VBox> </VBox>
</center> </center>
</BorderPane> </BorderPane>