Merge pull request #2513 from ripcurlx/create-dao-section-for-economy

Move BSQ dashboard into separate Facts & Figures section
This commit is contained in:
Christoph Atteneder 2019-03-27 15:44:19 +01:00 committed by GitHub
commit 2356d2eba2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1197 additions and 295 deletions

View file

@ -421,6 +421,10 @@ public class BSFormatter {
}
}
public String formatPrice(Price price, boolean appendCurrencyCode) {
return formatPrice(price, fiatPriceFormat, true);
}
public String formatPrice(Price price) {
return formatPrice(price, fiatPriceFormat, false);
}

View file

@ -124,6 +124,10 @@ public class BsqFormatter extends BSFormatter {
return super.formatCoin(satoshi, coinFormat);
}
public String formatBSQSatoshisWithCode(long satoshi) {
return super.formatCoinWithCode(satoshi, coinFormat);
}
public String formatBTCSatoshis(long satoshi) {
return super.formatCoin(satoshi, btcCoinFormat);
}

View file

@ -198,6 +198,7 @@ shared.actions=Actions
shared.buyerUpperCase=Buyer
shared.sellerUpperCase=Seller
shared.new=NEW
shared.new=NEW
####################################################################
# UI views
@ -1268,6 +1269,7 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=The lower price must
# DAO
####################################################################
dao.tab.factsAndFigures=Facts & Figures
dao.tab.bsqWallet=BSQ wallet
dao.tab.proposals=Governance
dao.tab.bonding=Bonding
@ -1755,29 +1757,6 @@ dao.wallet.menuItem.receive=Receive
dao.wallet.menuItem.transactions=Transactions
dao.wallet.dashboard.myBalance=My wallet balance
dao.wallet.dashboard.distribution=Distribution of all BSQ
dao.wallet.dashboard.locked=Global state of locked BSQ
dao.wallet.dashboard.market=Market data
dao.wallet.dashboard.genesis=Genesis transaction
dao.wallet.dashboard.txDetails=BSQ transactions statistics
dao.wallet.dashboard.genesisBlockHeight=Genesis block height
dao.wallet.dashboard.genesisTxId=Genesis transaction ID
dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction
dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests
dao.wallet.dashboard.reimbursementAmount=BSQ issued for reimbursement requests
dao.wallet.dashboard.availableAmount=Total available BSQ
dao.wallet.dashboard.burntAmount=Burned BSQ (fees)
dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds
dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds
dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds
dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds
dao.wallet.dashboard.allTx=No. of all BSQ transactions
dao.wallet.dashboard.utxo=No. of all unspent transaction outputs
dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions
dao.wallet.dashboard.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions
dao.wallet.dashboard.burntTx=No. of all fee payments transactions
dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq)
dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price)
dao.wallet.receive.fundYourWallet=Your BSQ receive address
dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address)
@ -1939,6 +1918,37 @@ dao.monitor.blindVote.table.hash=Hash of blind vote state
dao.monitor.blindVote.table.prev=Previous hash
dao.monitor.blindVote.table.numBlindVotes=No. blind votes
dao.factsAndFigures.menuItem.supply=BSQ Supply
dao.factsAndFigures.menuItem.transactions=BSQ Transactions
dao.factsAndFigures.dashboard.marketPrice=Market data
dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq)
dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price)
dao.factsAndFigures.dashboard.availableAmount=Total available BSQ
dao.factsAndFigures.supply.issued=BSQ issued
dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction
dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests
dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests
dao.factsAndFigures.supply.burnt=BSQ burnt
dao.factsAndFigures.supply.locked=Global state of locked BSQ
dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds
dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds
dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds
dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds
dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees)
dao.factsAndFigures.transactions.genesis=Genesis transaction
dao.factsAndFigures.transactions.genesisBlockHeight=Genesis block height
dao.factsAndFigures.transactions.genesisTxId=Genesis transaction ID
dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics
dao.factsAndFigures.transactions.allTx=No. of all BSQ transactions
dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs
dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions
dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions
dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions
####################################################################
# Windows

View file

@ -1682,17 +1682,21 @@ textfield */
* *
********************************************************************************************************************/
#charts .chart-legend {
.chart-pane {
-fx-background-color: -bs-rd-white;
}
#charts .chart-legend, #charts-dao .chart-legend {
-fx-font-size: 1.077em;
-fx-alignment: center;
}
#charts .axis, #price-chart .axis, #volume-chart .axis {
#charts .axis, #price-chart .axis, #volume-chart .axis, #charts-dao .axis {
-fx-tick-label-fill: -bs-rd-font-lighter;
-fx-tick-label-font-size: 0.769em;
}
#charts .chart-plot-background {
#charts .chart-plot-background, #charts-dao .chart-plot-background {
-fx-background-color: -bs-rd-white;
}
@ -1700,7 +1704,7 @@ textfield */
-fx-background-color: -bs-sell, -bs-rd-white;
}
#charts .default-color1.chart-area-symbol {
#charts .default-color1.chart-area-symbol, #charts-dao .default-color0.chart-area-symbol {
-fx-background-color: -bs-buy, -bs-rd-white;
}
@ -1708,7 +1712,7 @@ textfield */
-fx-stroke: -bs-sell;
}
#charts .default-color1.chart-series-area-line {
#charts .default-color1.chart-series-area-line, #charts-dao .default-color0.chart-series-area-line {
-fx-stroke: -bs-buy;
}
@ -1716,7 +1720,7 @@ textfield */
-fx-fill: -bs-sell-transparent;
}
#charts .default-color1.chart-series-area-fill {
#charts .default-color1.chart-series-area-fill, #charts-dao .default-color0.chart-series-area-fill {
-fx-fill: -bs-buy-transparent;
}
@ -2071,6 +2075,17 @@ textfield */
-fx-text-fill: -bs-rd-error-red;
}
.dao-kpi-big {
-fx-font-size: 1.923em;
-fx-text-fill: -bs-rd-black;
-fx-font-family: "IBM Plex Sans Light";
}
.dao-kpi-subtext {
-fx-text-fill: -bs-rd-font-light;
-fx-font-size: 0.923em;
}
/********************************************************************************************************************
* *
* Notifications *

View file

@ -27,11 +27,12 @@ import bisq.desktop.common.view.ViewLoader;
import bisq.desktop.main.MainView;
import bisq.desktop.main.dao.bonding.BondingView;
import bisq.desktop.main.dao.burnbsq.BurnBsqView;
import bisq.desktop.main.dao.economy.EconomyView;
import bisq.desktop.main.dao.governance.GovernanceView;
import bisq.desktop.main.dao.monitor.MonitorView;
import bisq.desktop.main.dao.news.NewsView;
import bisq.desktop.main.dao.wallet.BsqWalletView;
import bisq.desktop.main.dao.wallet.dashboard.BsqDashboardView;
import bisq.desktop.main.dao.wallet.send.BsqSendView;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.core.dao.governance.votereveal.VoteRevealService;
@ -53,7 +54,7 @@ import javafx.beans.value.ChangeListener;
public class DaoView extends ActivatableViewAndModel<TabPane, Activatable> {
@FXML
private Tab bsqWalletTab, proposalsTab, bondingTab, burnBsqTab, daoNewsTab, monitor;
private Tab bsqWalletTab, proposalsTab, bondingTab, burnBsqTab, daoNewsTab, monitorTab, factsAndFiguresTab;
private Navigation.Listener navigationListener;
private ChangeListener<Tab> tabChangeListener;
@ -77,36 +78,39 @@ public class DaoView extends ActivatableViewAndModel<TabPane, Activatable> {
@Override
public void initialize() {
factsAndFiguresTab = new Tab(Res.get("dao.tab.factsAndFigures").toUpperCase());
bsqWalletTab = new Tab(Res.get("dao.tab.bsqWallet").toUpperCase());
proposalsTab = new Tab(Res.get("dao.tab.proposals").toUpperCase());
bondingTab = new Tab(Res.get("dao.tab.bonding").toUpperCase());
burnBsqTab = new Tab(Res.get("dao.tab.proofOfBurn").toUpperCase());
monitor = new Tab(Res.get("dao.tab.monitor").toUpperCase());
monitorTab = new Tab(Res.get("dao.tab.monitor").toUpperCase());
factsAndFiguresTab.setClosable(false);
bsqWalletTab.setClosable(false);
proposalsTab.setClosable(false);
bondingTab.setClosable(false);
burnBsqTab.setClosable(false);
monitor.setClosable(false);
monitorTab.setClosable(false);
if (!DevEnv.isDaoActivated()) {
factsAndFiguresTab.setDisable(true);
bsqWalletTab.setDisable(true);
proposalsTab.setDisable(true);
bondingTab.setDisable(true);
burnBsqTab.setDisable(true);
monitor.setDisable(true);
monitorTab.setDisable(true);
daoNewsTab = new Tab(Res.get("dao.tab.news").toUpperCase());
root.getTabs().add(daoNewsTab);
} else {
root.getTabs().addAll(bsqWalletTab, proposalsTab, bondingTab, burnBsqTab, monitor);
root.getTabs().addAll(factsAndFiguresTab, bsqWalletTab, proposalsTab, bondingTab, burnBsqTab, monitorTab);
}
navigationListener = viewPath -> {
if (viewPath.size() == 3 && viewPath.indexOf(DaoView.class) == 1) {
if (proposalsTab == null && viewPath.get(2).equals(BsqWalletView.class))
navigation.navigateTo(MainView.class, DaoView.class, BsqWalletView.class);
if (proposalsTab == null && viewPath.get(2).equals(EconomyView.class))
navigation.navigateTo(MainView.class, DaoView.class, EconomyView.class);
else
loadView(viewPath.tip());
}
@ -114,9 +118,9 @@ public class DaoView extends ActivatableViewAndModel<TabPane, Activatable> {
tabChangeListener = (ov, oldValue, newValue) -> {
if (newValue == bsqWalletTab) {
Class<? extends View> selectedViewClass = bsqWalletView.getSelectedViewClass();
Class<? extends View> selectedViewClass = bsqWalletView != null ? bsqWalletView.getSelectedViewClass() : null;
if (selectedViewClass == null)
navigation.navigateTo(MainView.class, DaoView.class, BsqWalletView.class, BsqDashboardView.class);
navigation.navigateTo(MainView.class, DaoView.class, BsqWalletView.class, BsqSendView.class);
else
navigation.navigateTo(MainView.class, DaoView.class, BsqWalletView.class, selectedViewClass);
} else if (newValue == proposalsTab) {
@ -125,7 +129,9 @@ public class DaoView extends ActivatableViewAndModel<TabPane, Activatable> {
navigation.navigateTo(MainView.class, DaoView.class, BondingView.class);
} else if (newValue == burnBsqTab) {
navigation.navigateTo(MainView.class, DaoView.class, BurnBsqView.class);
} else if (newValue == monitor) {
} else if (newValue == factsAndFiguresTab) {
navigation.navigateTo(MainView.class, DaoView.class, EconomyView.class);
} else if (newValue == monitorTab) {
navigation.navigateTo(MainView.class, DaoView.class, MonitorView.class);
}
};
@ -147,7 +153,9 @@ public class DaoView extends ActivatableViewAndModel<TabPane, Activatable> {
navigation.navigateTo(MainView.class, DaoView.class, BondingView.class);
else if (selectedItem == burnBsqTab)
navigation.navigateTo(MainView.class, DaoView.class, BurnBsqView.class);
else if (selectedItem == monitor)
else if (selectedItem == factsAndFiguresTab)
navigation.navigateTo(MainView.class, DaoView.class, EconomyView.class);
else if (selectedItem == monitorTab)
navigation.navigateTo(MainView.class, DaoView.class, MonitorView.class);
}
} else {
@ -182,9 +190,11 @@ public class DaoView extends ActivatableViewAndModel<TabPane, Activatable> {
} else if (view instanceof BurnBsqView) {
selectedTab = burnBsqTab;
} else if (view instanceof MonitorView) {
selectedTab = monitor;
selectedTab = monitorTab;
} else if (view instanceof NewsView) {
selectedTab = daoNewsTab;
} else if (view instanceof EconomyView) {
selectedTab = factsAndFiguresTab;
}
selectedTab.setContent(view.getRoot());

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of Bisq.
~
~ Bisq is free software: you can redistribute it and/or modify it
~ under the terms of the GNU Affero General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or (at
~ your option) any later version.
~
~ Bisq is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
~ License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with Bisq. If not, see <http://www.gnu.org/licenses/>.
-->
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane fx:id="root" fx:controller="bisq.desktop.main.dao.economy.EconomyView"
xmlns:fx="http://javafx.com/fxml">
<VBox fx:id="leftVBox" prefWidth="240" spacing="5" AnchorPane.bottomAnchor="20" AnchorPane.leftAnchor="15"
AnchorPane.topAnchor="15"/>
<ScrollPane fitToWidth="true" fitToHeight="true"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="270.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<AnchorPane fx:id="content"/>
</ScrollPane>
</AnchorPane>

View file

@ -0,0 +1,136 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more supply.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main.dao.economy;
import bisq.desktop.Navigation;
import bisq.desktop.common.view.ActivatableViewAndModel;
import bisq.desktop.common.view.CachingViewLoader;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.common.view.View;
import bisq.desktop.common.view.ViewLoader;
import bisq.desktop.common.view.ViewPath;
import bisq.desktop.components.MenuItem;
import bisq.desktop.main.MainView;
import bisq.desktop.main.dao.DaoView;
import bisq.desktop.main.dao.economy.dashboard.BsqDashboardView;
import bisq.desktop.main.dao.economy.supply.SupplyView;
import bisq.desktop.main.dao.economy.transactions.BSQTransactionsView;
import bisq.core.locale.Res;
import bisq.common.app.DevEnv;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import java.util.Arrays;
import java.util.List;
@FxmlView
public class EconomyView extends ActivatableViewAndModel {
private final ViewLoader viewLoader;
private final Navigation navigation;
private MenuItem dashboard, supply, transactions;
private Navigation.Listener listener;
@FXML
private VBox leftVBox;
@FXML
private AnchorPane content;
private Class<? extends View> selectedViewClass;
private ToggleGroup toggleGroup;
@Inject
private EconomyView(CachingViewLoader viewLoader, Navigation navigation) {
this.viewLoader = viewLoader;
this.navigation = navigation;
}
@Override
public void initialize() {
listener = viewPath -> {
if (viewPath.size() != 4 || viewPath.indexOf(EconomyView.class) != 2)
return;
selectedViewClass = viewPath.tip();
loadView(selectedViewClass);
};
toggleGroup = new ToggleGroup();
List<Class<? extends View>> baseNavPath = Arrays.asList(MainView.class, DaoView.class, EconomyView.class);
dashboard = new MenuItem(navigation, toggleGroup, Res.get("shared.dashboard"), BsqDashboardView.class, baseNavPath);
supply = new MenuItem(navigation, toggleGroup, Res.get("dao.factsAndFigures.menuItem.supply"), SupplyView.class, baseNavPath);
transactions = new MenuItem(navigation, toggleGroup, Res.get("dao.factsAndFigures.menuItem.transactions"), BSQTransactionsView.class, baseNavPath);
leftVBox.getChildren().addAll(dashboard, supply, transactions);
// TODO just until DAO is enabled
if (!DevEnv.isDaoActivated()) {
dashboard.setDisable(true);
supply.setDisable(true);
transactions.setDisable(true);
}
}
@Override
protected void activate() {
dashboard.activate();
supply.activate();
transactions.activate();
navigation.addListener(listener);
ViewPath viewPath = navigation.getCurrentPath();
if (viewPath.size() == 3 && viewPath.indexOf(EconomyView.class) == 2 ||
viewPath.size() == 2 && viewPath.indexOf(DaoView.class) == 1) {
if (selectedViewClass == null)
selectedViewClass = BsqDashboardView.class;
loadView(selectedViewClass);
} else if (viewPath.size() == 4 && viewPath.indexOf(EconomyView.class) == 2) {
selectedViewClass = viewPath.get(3);
loadView(selectedViewClass);
}
}
@SuppressWarnings("Duplicates")
@Override
protected void deactivate() {
navigation.removeListener(listener);
dashboard.deactivate();
supply.deactivate();
transactions.deactivate();
}
private void loadView(Class<? extends View> viewClass) {
View view = viewLoader.load(viewClass);
content.getChildren().setAll(view.getRoot());
if (view instanceof BsqDashboardView) toggleGroup.selectToggle(dashboard);
else if (view instanceof SupplyView) toggleGroup.selectToggle(supply);
else if (view instanceof BSQTransactionsView) toggleGroup.selectToggle(transactions);
}
}

View file

@ -20,14 +20,13 @@
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<GridPane fx:id="root" fx:controller="bisq.desktop.main.dao.wallet.dashboard.BsqDashboardView"
<GridPane fx:id="root" fx:controller="bisq.desktop.main.dao.economy.dashboard.BsqDashboardView"
hgap="5.0" vgap="5.0"
AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="20.0"
AnchorPane.rightAnchor="25.0" AnchorPane.topAnchor="20.0"
xmlns:fx="http://javafx.com/fxml">
<columnConstraints>
<ColumnConstraints percentWidth="50"/>
<ColumnConstraints minWidth="10" maxWidth="5"/>
<ColumnConstraints percentWidth="50"/>
</columnConstraints>
</GridPane>

View file

@ -0,0 +1,324 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main.dao.economy.dashboard;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.util.FormBuilder;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.locale.Res;
import bisq.core.monetary.Price;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.TradeStatistics2;
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
import bisq.core.util.BSFormatter;
import bisq.core.util.BsqFormatter;
import bisq.common.util.Tuple3;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.geometry.Insets;
import javafx.geometry.Side;
import javafx.beans.value.ChangeListener;
import javafx.util.StringConverter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static bisq.desktop.util.FormBuilder.addLabelWithSubText;
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
import java.sql.Date;
@FxmlView
public class BsqDashboardView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private static final String DAY = "day";
private static final Map<String, TemporalAdjuster> ADJUSTERS = new HashMap<>();
private final DaoFacade daoFacade;
private final TradeStatisticsManager tradeStatisticsManager;
private final PriceFeedService priceFeedService;
private final DaoStateService daoStateService;
private final Preferences preferences;
private final BsqFormatter bsqFormatter;
private final BSFormatter btcFormatter;
private ChangeListener<Number> priceChangeListener;
private AreaChart bsqPriceChart;
private XYChart.Series<Number, Number> seriesBSQAdded, seriesBSQBurnt;
private XYChart.Series<Number, Number> seriesBSQPrice;
private TextField marketCapTextField, availableAmountTextField;
private Label marketPriceLabel;
private Coin availableAmount;
private int gridRow = 0;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private BsqDashboardView(DaoFacade daoFacade,
TradeStatisticsManager tradeStatisticsManager,
PriceFeedService priceFeedService,
DaoStateService daoStateService,
Preferences preferences,
BsqFormatter bsqFormatter,
BSFormatter btcFormatter) {
this.daoFacade = daoFacade;
this.tradeStatisticsManager = tradeStatisticsManager;
this.priceFeedService = priceFeedService;
this.daoStateService = daoStateService;
this.preferences = preferences;
this.bsqFormatter = bsqFormatter;
this.btcFormatter = btcFormatter;
}
@Override
public void initialize() {
ADJUSTERS.put(DAY, TemporalAdjusters.ofDateAdjuster(d -> d));
createKPIs();
createChart();
priceChangeListener = (observable, oldValue, newValue) -> updatePrice();
}
private void createKPIs() {
Tuple3<Label, Label, VBox> marketPriceBox = addLabelWithSubText(root, gridRow++, "0.004000 BSQ/BTC", "Latest BSQ/BTC trade price (in Bisq)");
marketPriceLabel = marketPriceBox.first;
marketPriceLabel.getStyleClass().add("dao-kpi-big");
marketPriceBox.second.getStyleClass().add("dao-kpi-subtext");
marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.dashboard.marketCap")).second;
availableAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.dashboard.availableAmount")).second;
}
@Override
protected void activate() {
daoFacade.addBsqStateListener(this);
priceFeedService.updateCounterProperty().addListener(priceChangeListener);
updateWithBsqBlockChainData();
updatePrice();
updateChartData();
}
@Override
protected void deactivate() {
daoFacade.removeBsqStateListener(this);
priceFeedService.updateCounterProperty().removeListener(priceChangeListener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
updateWithBsqBlockChainData();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void createChart() {
NumberAxis xAxis = new NumberAxis();
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(true);
xAxis.setTickLabelGap(6);
xAxis.setTickMarkVisible(false);
xAxis.setMinorTickVisible(false);
xAxis.setTickLabelFormatter(new StringConverter<>() {
@Override
public String toString(Number timestamp) {
LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(timestamp.longValue(),
0, OffsetDateTime.now(ZoneId.systemDefault()).getOffset());
return localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM));
}
@Override
public Number fromString(String string) {
return 0;
}
});
NumberAxis yAxis = new NumberAxis();
yAxis.setForceZeroInRange(false);
yAxis.setSide(Side.RIGHT);
yAxis.setAutoRanging(true);
yAxis.setTickMarkVisible(false);
yAxis.setMinorTickVisible(false);
yAxis.setTickLabelGap(5);
yAxis.setTickLabelFormatter(new StringConverter<>() {
@Override
public String toString(Number marketPrice) {
return bsqFormatter.formatBTCWithCode(marketPrice.longValue());
}
@Override
public Number fromString(String string) {
return 0;
}
});
seriesBSQPrice = new XYChart.Series<>();
seriesBSQPrice.setName("Price in BTC for 1 BSQ");
bsqPriceChart = new AreaChart<>(xAxis, yAxis);
bsqPriceChart.setLegendVisible(false);
bsqPriceChart.setAnimated(false);
bsqPriceChart.setId("charts-dao");
bsqPriceChart.setMinHeight(385);
bsqPriceChart.setPrefHeight(385);
bsqPriceChart.setCreateSymbols(true);
bsqPriceChart.setPadding(new Insets(0));
bsqPriceChart.getData().addAll(seriesBSQPrice);
AnchorPane chartPane = new AnchorPane();
chartPane.getStyleClass().add("chart-pane");
AnchorPane.setTopAnchor(bsqPriceChart, 15d);
AnchorPane.setBottomAnchor(bsqPriceChart, 10d);
AnchorPane.setLeftAnchor(bsqPriceChart, 25d);
AnchorPane.setRightAnchor(bsqPriceChart, 10d);
chartPane.getChildren().add(bsqPriceChart);
GridPane.setRowIndex(chartPane, ++gridRow);
GridPane.setColumnSpan(chartPane, 2);
GridPane.setMargin(chartPane, new Insets(10, 0, 0, 0));
root.getChildren().addAll(chartPane);
}
private void updateChartData() {
updateBSQPriceData();
}
private void updateBSQPriceData() {
seriesBSQPrice.getData().clear();
Map<LocalDate, List<TradeStatistics2>> bsqPriceByDate = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
.filter(e -> e.getCurrencyCode().equals("BSQ"))
.sorted(Comparator.comparing(TradeStatistics2::getTradeDate))
.collect(Collectors.groupingBy(item -> new Date(item.getTradeDate().getTime()).toLocalDate()
.with(ADJUSTERS.get(DAY))));
List<XYChart.Data<Number, Number>> updatedBSQPrice = bsqPriceByDate.keySet().stream()
.map(e -> {
ZonedDateTime zonedDateTime = e.atStartOfDay(ZoneId.systemDefault());
return new XYChart.Data<Number, Number>(zonedDateTime.toInstant().getEpochSecond(), bsqPriceByDate.get(e).stream()
.map(TradeStatistics2::getTradePrice)
.mapToDouble(Price::getValue)
.average()
.orElse(Double.NaN)
);
})
.collect(Collectors.toList());
seriesBSQPrice.getData().setAll(updatedBSQPrice);
}
private void updateWithBsqBlockChainData() {
Coin issuedAmountFromGenesis = daoFacade.getGenesisTotalSupply();
Coin issuedAmountFromCompRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.COMPENSATION));
Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT));
Coin burntFee = Coin.valueOf(daoFacade.getTotalBurntFee());
Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs());
availableAmount = issuedAmountFromGenesis
.add(issuedAmountFromCompRequests)
.add(issuedAmountFromReimbursementRequests)
.subtract(burntFee)
.subtract(totalConfiscatedAmount);
availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount));
}
private void updatePrice() {
Optional<Price> optionalBsqPrice = priceFeedService.getBsqPrice();
if (optionalBsqPrice.isPresent()) {
Price bsqPrice = optionalBsqPrice.get();
marketPriceLabel.setText(bsqFormatter.formatPrice(bsqPrice) + " BSQ/BTC");
marketCapTextField.setText(bsqFormatter.formatMarketCap(priceFeedService.getMarketPrice("BSQ"),
priceFeedService.getMarketPrice(preferences.getPreferredTradeCurrency().getCode()),
availableAmount));
updateChartData();
} else {
marketPriceLabel.setText(Res.get("shared.na"));
marketCapTextField.setText(Res.get("shared.na"));
}
}
}

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of Bisq.
~
~ Bisq is free software: you can redistribute it and/or modify it
~ under the terms of the GNU Affero General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or (at
~ your option) any later version.
~
~ Bisq is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
~ License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with Bisq. If not, see <http://www.gnu.org/licenses/>.
-->
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<GridPane fx:id="root" fx:controller="bisq.desktop.main.dao.economy.supply.SupplyView"
hgap="5.0" vgap="5.0"
AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="20.0"
AnchorPane.rightAnchor="25.0" AnchorPane.topAnchor="20.0"
xmlns:fx="http://javafx.com/fxml">
<columnConstraints>
<ColumnConstraints percentWidth="50"/>
<ColumnConstraints percentWidth="50"/>
</columnConstraints>
</GridPane>

View file

@ -0,0 +1,335 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main.dao.economy.supply;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.util.Layout;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.blockchain.Tx;
import bisq.core.dao.state.model.governance.Issuance;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.locale.GlobalSettings;
import bisq.core.locale.Res;
import bisq.core.util.BsqFormatter;
import bisq.common.util.Tuple3;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.geometry.Insets;
import javafx.geometry.Side;
import javafx.util.StringConverter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
import java.sql.Date;
@FxmlView
public class SupplyView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private static final String MONTH = "month";
private final DaoFacade daoFacade;
private DaoStateService daoStateService;
private final BsqFormatter bsqFormatter;
private int gridRow = 0;
private TextField genesisIssueAmountTextField, compRequestIssueAmountTextField, reimbursementAmountTextField,
burntAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField,
totalUnlockedAmountTextField, totalConfiscatedAmountTextField;
private XYChart.Series<Number, Number> seriesBSQIssued, seriesBSQBurnt;
private static final Map<String, TemporalAdjuster> ADJUSTERS = new HashMap<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private SupplyView(DaoFacade daoFacade,
DaoStateService daoStateService,
BsqFormatter bsqFormatter) {
this.daoFacade = daoFacade;
this.daoStateService = daoStateService;
this.bsqFormatter = bsqFormatter;
}
@Override
public void initialize() {
ADJUSTERS.put(MONTH, TemporalAdjusters.firstDayOfMonth());
createSupplyIncreasedInformation();
createSupplyReducedInformation();
createSupplyLockedInformation();
}
@Override
protected void activate() {
daoFacade.addBsqStateListener(this);
updateWithBsqBlockChainData();
}
@Override
protected void deactivate() {
daoFacade.removeBsqStateListener(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
updateWithBsqBlockChainData();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void createSupplyIncreasedInformation() {
addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.factsAndFigures.supply.issued"));
Tuple3<Label, TextField, VBox> genesisAmountTuple = addTopLabelReadOnlyTextField(root, gridRow,
Res.get("dao.factsAndFigures.supply.genesisIssueAmount"), Layout.FIRST_ROW_DISTANCE);
genesisIssueAmountTextField = genesisAmountTuple.second;
GridPane.setColumnSpan(genesisAmountTuple.third, 2);
compRequestIssueAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.supply.compRequestIssueAmount")).second;
reimbursementAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.supply.reimbursementAmount")).second;
seriesBSQIssued = new XYChart.Series<>();
createChart(seriesBSQIssued, Res.get("dao.factsAndFigures.supply.issued"));
}
private void createSupplyReducedInformation() {
addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.factsAndFigures.supply.burnt"), Layout.GROUP_DISTANCE);
Tuple3<Label, TextField, VBox> burntAmountTuple = addTopLabelReadOnlyTextField(root, gridRow,
Res.get("dao.factsAndFigures.supply.burntAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
burntAmountTextField = burntAmountTuple.second;
GridPane.setColumnSpan(burntAmountTuple.third, 2);
seriesBSQBurnt = new XYChart.Series<>();
createChart(seriesBSQBurnt, Res.get("dao.factsAndFigures.supply.burnt"));
}
private void createSupplyLockedInformation() {
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.factsAndFigures.supply.locked"), Layout.GROUP_DISTANCE);
titledGroupBg.getStyleClass().add("last");
totalLockedUpAmountTextField = addTopLabelReadOnlyTextField(root, gridRow,
Res.get("dao.factsAndFigures.supply.totalLockedUpAmount"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
totalUnlockingAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.supply.totalUnlockingAmount"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
totalUnlockedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.supply.totalUnlockedAmount")).second;
totalConfiscatedAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.supply.totalConfiscatedAmount")).second;
}
private void createChart(XYChart.Series<Number, Number> series, String seriesLabel) {
NumberAxis xAxis = new NumberAxis();
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(true);
xAxis.setTickLabelGap(6);
xAxis.setTickMarkVisible(false);
xAxis.setMinorTickVisible(false);
xAxis.setTickLabelFormatter(new StringConverter<>() {
@Override
public String toString(Number timestamp) {
LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(timestamp.longValue(),
0, OffsetDateTime.now(ZoneId.systemDefault()).getOffset());
return localDateTime.format(DateTimeFormatter.ofPattern("MMM uu", GlobalSettings.getLocale()));
}
@Override
public Number fromString(String string) {
return 0;
}
});
NumberAxis yAxis = new NumberAxis();
yAxis.setForceZeroInRange(false);
yAxis.setSide(Side.RIGHT);
yAxis.setAutoRanging(true);
yAxis.setTickMarkVisible(false);
yAxis.setMinorTickVisible(false);
yAxis.setTickLabelGap(5);
yAxis.setTickLabelFormatter(new StringConverter<>() {
@Override
public String toString(Number marketPrice) {
return bsqFormatter.formatBSQSatoshisWithCode(marketPrice.longValue());
}
@Override
public Number fromString(String string) {
return 0;
}
});
series.setName(seriesLabel);
AreaChart<Number, Number> chart = new AreaChart<>(xAxis, yAxis);
chart.setLegendVisible(false);
chart.setAnimated(false);
chart.setId("charts-dao");
chart.setMinHeight(250);
chart.setPrefHeight(250);
chart.setCreateSymbols(true);
chart.setPadding(new Insets(0));
chart.getData().addAll(series);
AnchorPane chartPane = new AnchorPane();
chartPane.getStyleClass().add("chart-pane");
AnchorPane.setTopAnchor(chart, 15d);
AnchorPane.setBottomAnchor(chart, 10d);
AnchorPane.setLeftAnchor(chart, 25d);
AnchorPane.setRightAnchor(chart, 10d);
chartPane.getChildren().add(chart);
GridPane.setColumnSpan(chartPane, 2);
GridPane.setRowIndex(chartPane, ++gridRow);
GridPane.setMargin(chartPane, new Insets(10, 0, 0, 0));
root.getChildren().add(chartPane);
}
private void updateWithBsqBlockChainData() {
Coin issuedAmountFromGenesis = daoFacade.getGenesisTotalSupply();
genesisIssueAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromGenesis));
Coin issuedAmountFromCompRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.COMPENSATION));
compRequestIssueAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromCompRequests));
Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT));
reimbursementAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromReimbursementRequests));
Coin burntFee = Coin.valueOf(daoFacade.getTotalBurntFee());
Coin totalLockedUpAmount = Coin.valueOf(daoFacade.getTotalLockupAmount());
Coin totalUnlockingAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockingTxOutputs());
Coin totalUnlockedAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockedTxOutputs());
Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs());
burntAmountTextField.setText("-" + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee));
totalLockedUpAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalLockedUpAmount));
totalUnlockingAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockingAmount));
totalUnlockedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockedAmount));
totalConfiscatedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalConfiscatedAmount));
updateCharts();
}
private void updateCharts() {
seriesBSQIssued.getData().clear();
seriesBSQBurnt.getData().clear();
Map<LocalDate, List<Tx>> feesBurntByMonth = daoStateService.getBurntFeeTxs().stream()
.sorted(Comparator.comparing(Tx::getTime))
.collect(Collectors.groupingBy(item -> new Date(item.getTime()).toLocalDate()
.with(ADJUSTERS.get(MONTH))));
List<XYChart.Data<Number, Number>> updatedBurntBSQ = feesBurntByMonth.keySet().stream()
.map(date -> {
ZonedDateTime zonedDateTime = date.atStartOfDay(ZoneId.systemDefault());
return new XYChart.Data<Number, Number>(zonedDateTime.toInstant().getEpochSecond(), feesBurntByMonth.get(date)
.stream()
.mapToDouble(Tx::getBurntFee)
.sum()
);
})
.collect(Collectors.toList());
seriesBSQBurnt.getData().setAll(updatedBurntBSQ);
Stream<Issuance> bsqByCompensation = daoStateService.getIssuanceSet(IssuanceType.COMPENSATION).stream()
.sorted(Comparator.comparing(Issuance::getChainHeight));
Stream<Issuance> bsqByReImbursement = daoStateService.getIssuanceSet(IssuanceType.REIMBURSEMENT).stream()
.sorted(Comparator.comparing(Issuance::getChainHeight));
Map<LocalDate, List<Issuance>> bsqAddedByVote = Stream.concat(bsqByCompensation, bsqByReImbursement)
.collect(Collectors.groupingBy(item -> new Date(daoFacade.getBlockTime(item.getChainHeight())).toLocalDate()
.with(ADJUSTERS.get(MONTH))));
List<XYChart.Data<Number, Number>> updatedAddedBSQ = bsqAddedByVote.keySet().stream()
.map(date -> {
ZonedDateTime zonedDateTime = date.atStartOfDay(ZoneId.systemDefault());
return new XYChart.Data<Number, Number>(zonedDateTime.toInstant().getEpochSecond(), bsqAddedByVote.get(date)
.stream()
.mapToDouble(Issuance::getAmount)
.sum());
})
.collect(Collectors.toList());
seriesBSQIssued.getData().setAll(updatedAddedBSQ);
}
}

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of Bisq.
~
~ Bisq is free software: you can redistribute it and/or modify it
~ under the terms of the GNU Affero General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or (at
~ your option) any later version.
~
~ Bisq is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
~ License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with Bisq. If not, see <http://www.gnu.org/licenses/>.
-->
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<GridPane fx:id="root" fx:controller="bisq.desktop.main.dao.economy.transactions.BSQTransactionsView"
hgap="5.0" vgap="5.0"
AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="20.0"
AnchorPane.rightAnchor="25.0" AnchorPane.topAnchor="20.0"
xmlns:fx="http://javafx.com/fxml">
<columnConstraints>
<ColumnConstraints percentWidth="50"/>
<ColumnConstraints percentWidth="50"/>
</columnConstraints>
</GridPane>

View file

@ -0,0 +1,149 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main.dao.economy.transactions;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.util.Layout;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.locale.Res;
import bisq.core.user.Preferences;
import bisq.common.util.Tuple3;
import javax.inject.Inject;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
import static bisq.desktop.util.FormBuilder.addTopLabelHyperlinkWithIcon;
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
@FxmlView
public class BSQTransactionsView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private final DaoFacade daoFacade;
private final Preferences preferences;
private int gridRow = 0;
private TextField allTxTextField, burntTxTextField,
utxoTextField, compensationIssuanceTxTextField,
reimbursementIssuanceTxTextField;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private BSQTransactionsView(DaoFacade daoFacade,
Preferences preferences) {
this.daoFacade = daoFacade;
this.preferences = preferences;
}
@Override
public void initialize() {
addTitledGroupBg(root, gridRow, 2, Res.get("dao.factsAndFigures.transactions.genesis"));
String genTxHeight = String.valueOf(daoFacade.getGenesisBlockHeight());
String genesisTxId = daoFacade.getGenesisTxId();
String url = preferences.getBsqBlockChainExplorer().txUrl + genesisTxId;
GridPane.setColumnSpan(addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.factsAndFigures.transactions.genesisBlockHeight"),
genTxHeight, Layout.FIRST_ROW_DISTANCE).third, 2);
// TODO use addTopLabelTxIdTextField
Tuple3<Label, HyperlinkWithIcon, VBox> tuple = addTopLabelHyperlinkWithIcon(root, ++gridRow,
Res.get("dao.factsAndFigures.transactions.genesisTxId"), genesisTxId, url, 0);
HyperlinkWithIcon hyperlinkWithIcon = tuple.second;
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", genesisTxId)));
GridPane.setColumnSpan(tuple.third, 2);
int startRow = ++gridRow;
TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 3, Res.get("dao.factsAndFigures.transactions.txDetails"), Layout.GROUP_DISTANCE);
titledGroupBg.getStyleClass().add("last");
allTxTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.factsAndFigures.transactions.allTx"),
genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
utxoTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.factsAndFigures.transactions.utxo")).second;
compensationIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.transactions.compensationIssuanceTx")).second;
int columnIndex = 1;
gridRow = startRow;
titledGroupBg = addTitledGroupBg(root, startRow, columnIndex, 3, "", Layout.GROUP_DISTANCE);
titledGroupBg.getStyleClass().add("last");
reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex,
Res.get("dao.factsAndFigures.transactions.reimbursementIssuanceTx"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
burntTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex,
Res.get("dao.factsAndFigures.transactions.burntTx")).second;
}
@Override
protected void activate() {
daoFacade.addBsqStateListener(this);
updateWithBsqBlockChainData();
}
@Override
protected void deactivate() {
daoFacade.removeBsqStateListener(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
updateWithBsqBlockChainData();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void updateWithBsqBlockChainData() {
allTxTextField.setText(String.valueOf(daoFacade.getTxs().size()));
utxoTextField.setText(String.valueOf(daoFacade.getUnspentTxOutputs().size()));
compensationIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.COMPENSATION)));
reimbursementIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.REIMBURSEMENT)));
burntTxTextField.setText(String.valueOf(daoFacade.getFeeTxs().size()));
}
}

View file

@ -27,7 +27,6 @@ import bisq.desktop.common.view.ViewPath;
import bisq.desktop.components.MenuItem;
import bisq.desktop.main.MainView;
import bisq.desktop.main.dao.DaoView;
import bisq.desktop.main.dao.wallet.dashboard.BsqDashboardView;
import bisq.desktop.main.dao.wallet.receive.BsqReceiveView;
import bisq.desktop.main.dao.wallet.send.BsqSendView;
import bisq.desktop.main.dao.wallet.tx.BsqTxView;
@ -53,7 +52,7 @@ public class BsqWalletView extends ActivatableViewAndModel {
private final ViewLoader viewLoader;
private final Navigation navigation;
private MenuItem dashboard, send, receive, transactions;
private MenuItem send, receive, transactions;
private Navigation.Listener listener;
@FXML
@ -82,15 +81,13 @@ public class BsqWalletView extends ActivatableViewAndModel {
toggleGroup = new ToggleGroup();
List<Class<? extends View>> baseNavPath = Arrays.asList(MainView.class, DaoView.class, BsqWalletView.class);
dashboard = new MenuItem(navigation, toggleGroup, Res.get("shared.dashboard"), BsqDashboardView.class, baseNavPath);
send = new MenuItem(navigation, toggleGroup, Res.get("dao.wallet.menuItem.send"), BsqSendView.class, baseNavPath);
receive = new MenuItem(navigation, toggleGroup, Res.get("dao.wallet.menuItem.receive"), BsqReceiveView.class, baseNavPath);
transactions = new MenuItem(navigation, toggleGroup, Res.get("dao.wallet.menuItem.transactions"), BsqTxView.class, baseNavPath);
leftVBox.getChildren().addAll(dashboard, send, receive, transactions);
leftVBox.getChildren().addAll(send, receive, transactions);
// TODO just until DAO is enabled
if (!DevEnv.isDaoActivated()) {
dashboard.setDisable(true);
send.setDisable(true);
transactions.setDisable(true);
}
@ -98,7 +95,6 @@ public class BsqWalletView extends ActivatableViewAndModel {
@Override
protected void activate() {
dashboard.activate();
send.activate();
receive.activate();
transactions.activate();
@ -108,7 +104,7 @@ public class BsqWalletView extends ActivatableViewAndModel {
if (viewPath.size() == 3 && viewPath.indexOf(BsqWalletView.class) == 2 ||
viewPath.size() == 2 && viewPath.indexOf(DaoView.class) == 1) {
if (selectedViewClass == null)
selectedViewClass = BsqDashboardView.class;
selectedViewClass = BsqSendView.class;
// TODO just until DAO is enabled
if (!DevEnv.isDaoActivated())
@ -126,7 +122,6 @@ public class BsqWalletView extends ActivatableViewAndModel {
protected void deactivate() {
navigation.removeListener(listener);
dashboard.deactivate();
send.deactivate();
receive.deactivate();
transactions.deactivate();
@ -136,8 +131,7 @@ public class BsqWalletView extends ActivatableViewAndModel {
View view = viewLoader.load(viewClass);
content.getChildren().setAll(view.getRoot());
if (view instanceof BsqDashboardView) toggleGroup.selectToggle(dashboard);
else if (view instanceof BsqSendView) toggleGroup.selectToggle(send);
if (view instanceof BsqSendView) toggleGroup.selectToggle(send);
else if (view instanceof BsqReceiveView) toggleGroup.selectToggle(receive);
else if (view instanceof BsqTxView) toggleGroup.selectToggle(transactions);
}

View file

@ -1,236 +0,0 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main.dao.wallet.dashboard;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.dao.wallet.BsqBalanceUtil;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.Layout;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.locale.Res;
import bisq.core.monetary.Price;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.Preferences;
import bisq.core.util.BsqFormatter;
import bisq.common.util.Tuple3;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.beans.value.ChangeListener;
import java.util.Optional;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
@FxmlView
public class BsqDashboardView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private final BsqBalanceUtil bsqBalanceUtil;
private final DaoFacade daoFacade;
private final PriceFeedService priceFeedService;
private final Preferences preferences;
private final BsqFormatter bsqFormatter;
private int gridRow = 0;
private TextField genesisIssueAmountTextField, compRequestIssueAmountTextField, reimbursementAmountTextField, availableAmountTextField,
burntAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField,
totalUnlockedAmountTextField, totalConfiscatedAmountTextField, allTxTextField, burntTxTextField,
utxoTextField, compensationIssuanceTxTextField,
reimbursementIssuanceTxTextField, priceTextField, marketCapTextField;
private ChangeListener<Number> priceChangeListener;
private Coin availableAmount;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private BsqDashboardView(BsqBalanceUtil bsqBalanceUtil,
DaoFacade daoFacade,
PriceFeedService priceFeedService,
Preferences preferences,
BsqFormatter bsqFormatter) {
this.bsqBalanceUtil = bsqBalanceUtil;
this.daoFacade = daoFacade;
this.priceFeedService = priceFeedService;
this.preferences = preferences;
this.bsqFormatter = bsqFormatter;
}
@Override
public void initialize() {
gridRow = bsqBalanceUtil.addGroup(root, gridRow);
int columnIndex = 2;
int startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 5, Res.get("dao.wallet.dashboard.distribution"), Layout.GROUP_DISTANCE);
genesisIssueAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisIssueAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
compRequestIssueAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compRequestIssueAmount")).second;
reimbursementAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.reimbursementAmount")).second;
burntAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second;
availableAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second;
gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 5, Res.get("dao.wallet.dashboard.locked"), Layout.GROUP_DISTANCE);
totalLockedUpAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalLockedUpAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
totalUnlockingAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockingAmount")).second;
totalUnlockedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second;
totalConfiscatedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalConfiscatedAmount")).second;
gridRow++;
startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.wallet.dashboard.market"), Layout.GROUP_DISTANCE);
priceTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.price"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second;
gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 2, Res.get("dao.wallet.dashboard.genesis"), Layout.GROUP_DISTANCE);
String genTxHeight = String.valueOf(daoFacade.getGenesisBlockHeight());
String genesisTxId = daoFacade.getGenesisTxId();
String url = preferences.getBsqBlockChainExplorer().txUrl + genesisTxId;
addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.genesisBlockHeight"),
genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE);
// TODO use addTopLabelTxIdTextField
Tuple3<Label, HyperlinkWithIcon, VBox> tuple = FormBuilder.addTopLabelHyperlinkWithIcon(root, ++gridRow, columnIndex,
Res.get("dao.wallet.dashboard.genesisTxId"), genesisTxId, url, 0);
HyperlinkWithIcon hyperlinkWithIcon = tuple.second;
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", genesisTxId)));
startRow = gridRow;
TitledGroupBg titledGroupBgTxDetails = addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.dashboard.txDetails"), Layout.GROUP_DISTANCE);
titledGroupBgTxDetails.getStyleClass().add("last");
allTxTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.allTx"),
genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
utxoTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second;
compensationIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.wallet.dashboard.compensationIssuanceTx")).second;
gridRow = startRow;
TitledGroupBg titledGroupBgReImbursement = addTitledGroupBg(root, ++gridRow, columnIndex, 3, "", Layout.GROUP_DISTANCE);
titledGroupBgReImbursement.getStyleClass().add("last");
reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex,
Res.get("dao.wallet.dashboard.reimbursementIssuanceTx"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
burntTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex,
Res.get("dao.wallet.dashboard.burntTx")).second;
++gridRow;
priceChangeListener = (observable, oldValue, newValue) -> updatePrice();
}
@Override
protected void activate() {
bsqBalanceUtil.activate();
daoFacade.addBsqStateListener(this);
priceFeedService.updateCounterProperty().addListener(priceChangeListener);
updateWithBsqBlockChainData();
updatePrice();
}
@Override
protected void deactivate() {
bsqBalanceUtil.deactivate();
daoFacade.removeBsqStateListener(this);
priceFeedService.updateCounterProperty().removeListener(priceChangeListener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
updateWithBsqBlockChainData();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void updateWithBsqBlockChainData() {
Coin issuedAmountFromGenesis = daoFacade.getGenesisTotalSupply();
genesisIssueAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromGenesis));
Coin issuedAmountFromCompRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.COMPENSATION));
compRequestIssueAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromCompRequests));
Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT));
reimbursementAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromReimbursementRequests));
Coin burntFee = Coin.valueOf(daoFacade.getTotalBurntFee());
Coin totalLockedUpAmount = Coin.valueOf(daoFacade.getTotalLockupAmount());
Coin totalUnlockingAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockingTxOutputs());
Coin totalUnlockedAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockedTxOutputs());
Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs());
availableAmount = issuedAmountFromGenesis
.add(issuedAmountFromCompRequests)
.add(issuedAmountFromReimbursementRequests)
.subtract(burntFee)
.subtract(totalConfiscatedAmount);
availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount));
burntAmountTextField.setText("-" + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee));
totalLockedUpAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalLockedUpAmount));
totalUnlockingAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockingAmount));
totalUnlockedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockedAmount));
totalConfiscatedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalConfiscatedAmount));
allTxTextField.setText(String.valueOf(daoFacade.getTxs().size()));
utxoTextField.setText(String.valueOf(daoFacade.getUnspentTxOutputs().size()));
compensationIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.COMPENSATION)));
reimbursementIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.REIMBURSEMENT)));
burntTxTextField.setText(String.valueOf(daoFacade.getFeeTxs().size()));
}
private void updatePrice() {
Optional<Price> optionalBsqPrice = priceFeedService.getBsqPrice();
if (optionalBsqPrice.isPresent()) {
Price bsqPrice = optionalBsqPrice.get();
priceTextField.setText(bsqFormatter.formatPrice(bsqPrice) + " BSQ/BTC");
marketCapTextField.setText(bsqFormatter.formatMarketCap(priceFeedService.getMarketPrice("BSQ"),
priceFeedService.getMarketPrice(preferences.getPreferredTradeCurrency().getCode()),
availableAmount));
} else {
priceTextField.setText(Res.get("shared.na"));
marketCapTextField.setText(Res.get("shared.na"));
}
}
}

View file

@ -61,6 +61,7 @@ import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
@ -106,6 +107,7 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
private TableView<OfferListItem> buyOfferTableView;
private TableView<OfferListItem> sellOfferTableView;
private AreaChart<Number, Number> areaChart;
private AnchorPane chartPane;
private ComboBox<CurrencyListItem> currencyComboBox;
private Subscription tradeCurrencySubscriber;
private final StringProperty volumeColumnLabel = new SimpleStringProperty();
@ -154,6 +156,8 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
createChart();
VBox.setMargin(chartPane, new Insets(0, 0, 5, 0));
Tuple4<TableView<OfferListItem>, VBox, Button, Label> tupleBuy = getOfferTable(OfferPayload.Direction.BUY);
Tuple4<TableView<OfferListItem>, VBox, Button, Label> tupleSell = getOfferTable(OfferPayload.Direction.SELL);
buyOfferTableView = tupleBuy.first;
@ -175,7 +179,8 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
tupleSell.second.setUserData(OfferPayload.Direction.SELL.name());
bottomHBox.getChildren().addAll(tupleBuy.second, tupleSell.second);
root.getChildren().addAll(currencyComboBoxTuple.first, areaChart, bottomHBox);
root.getChildren().addAll(currencyComboBoxTuple.first, chartPane, bottomHBox);
}
@Override
@ -334,11 +339,21 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
areaChart.setLegendVisible(false);
areaChart.setAnimated(false);
areaChart.setId("charts");
areaChart.setMinHeight(300);
areaChart.setPrefHeight(300);
areaChart.setCreateSymbols(false);
areaChart.setMinHeight(270);
areaChart.setPrefHeight(270);
areaChart.setCreateSymbols(true);
areaChart.setPadding(new Insets(0, 10, 0, 10));
areaChart.getData().addAll(seriesBuy, seriesSell);
chartPane = new AnchorPane();
chartPane.getStyleClass().add("chart-pane");
AnchorPane.setTopAnchor(areaChart, 15d);
AnchorPane.setBottomAnchor(areaChart, 10d);
AnchorPane.setLeftAnchor(areaChart, 10d);
AnchorPane.setRightAnchor(areaChart, 0d);
chartPane.getChildren().add(areaChart);
}
private void updateChartData() {

View file

@ -59,6 +59,7 @@ import javafx.scene.control.TableView;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
@ -126,6 +127,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
private ChangeListener<Number> parentHeightListener;
private Pane rootParent;
private ChangeListener<String> priceColumnLabelListener;
private AnchorPane priceChartPane, volumeChartPane;
///////////////////////////////////////////////////////////////////////////////////////////
@ -152,7 +154,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
nrOfTradeStatisticsLabel = new AutoTooltipLabel(" "); // set empty string for layout
nrOfTradeStatisticsLabel.setId("num-offers");
nrOfTradeStatisticsLabel.setPadding(new Insets(-5, 0, -10, 5));
root.getChildren().addAll(toolBox, priceChart, volumeChart, tableView, nrOfTradeStatisticsLabel);
root.getChildren().addAll(toolBox, priceChartPane, volumeChartPane, tableView, nrOfTradeStatisticsLabel);
timeUnitChangeListener = (observable, oldValue, newValue) -> {
if (newValue != null) {
@ -348,6 +350,15 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
//noinspection unchecked
priceChart.setData(FXCollections.observableArrayList(priceSeries));
priceChartPane = new AnchorPane();
priceChartPane.getStyleClass().add("chart-pane");
AnchorPane.setTopAnchor(priceChart, 15d);
AnchorPane.setBottomAnchor(priceChart, 10d);
AnchorPane.setLeftAnchor(priceChart, 0d);
AnchorPane.setRightAnchor(priceChart, 10d);
priceChartPane.getChildren().add(priceChart);
volumeSeries = new XYChart.Series<>();
@ -392,6 +403,16 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
volumeChart.setMaxHeight(200);
volumeChart.setLegendVisible(false);
volumeChart.setPadding(new Insets(0));
volumeChartPane = new AnchorPane();
volumeChartPane.getStyleClass().add("chart-pane");
AnchorPane.setTopAnchor(volumeChart, 15d);
AnchorPane.setBottomAnchor(volumeChart, 10d);
AnchorPane.setLeftAnchor(volumeChart, 0d);
AnchorPane.setRightAnchor(volumeChart, 10d);
volumeChartPane.getChildren().add(volumeChart);
}
private void updateChartData() {

View file

@ -141,6 +141,28 @@ public class FormBuilder {
return label;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Label + Subtext
///////////////////////////////////////////////////////////////////////////////////////////
public static Tuple3<Label, Label, VBox> addLabelWithSubText(GridPane gridPane, int rowIndex, String title, String description) {
return addLabelWithSubText(gridPane, rowIndex, title, description, 0);
}
public static Tuple3<Label, Label, VBox> addLabelWithSubText(GridPane gridPane, int rowIndex, String title, String description, double top) {
Label label = new AutoTooltipLabel(title);
Label subText = new AutoTooltipLabel(description);
VBox vBox = new VBox();
vBox.getChildren().setAll(label, subText);
GridPane.setRowIndex(vBox, rowIndex);
GridPane.setMargin(vBox, new Insets(top, 0, 0, 0));
gridPane.getChildren().add(vBox);
return new Tuple3<>(label, subText, vBox);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Multiline Label