Merge pull request #2566 from ripcurlx/ui-dao-improvements

DAO UI improvements
This commit is contained in:
Manfred Karrer 2019-03-25 18:34:02 -05:00 committed by GitHub
commit 2a46133292
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 878 additions and 472 deletions

View file

@ -1681,6 +1681,7 @@ dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal
The already paid proposal fee will be lost. The already paid proposal fee will be lost.
dao.proposal.active.remove.doRemove=Yes, remove my proposal dao.proposal.active.remove.doRemove=Yes, remove my proposal
dao.proposal.active.remove.failed=Could not remove proposal. dao.proposal.active.remove.failed=Could not remove proposal.
dao.proposal.myVote.title=Voting
dao.proposal.myVote.accept=Accept proposal dao.proposal.myVote.accept=Accept proposal
dao.proposal.myVote.reject=Reject proposal dao.proposal.myVote.reject=Reject proposal
dao.proposal.myVote.removeMyVote=Ignore proposal dao.proposal.myVote.removeMyVote=Ignore proposal
@ -1688,10 +1689,12 @@ dao.proposal.myVote.merit=Vote weight from earned BSQ
dao.proposal.myVote.stake=Vote weight from stake dao.proposal.myVote.stake=Vote weight from stake
dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID
dao.proposal.myVote.revealTxId=Vote reveal transaction ID dao.proposal.myVote.revealTxId=Vote reveal transaction ID
dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0} dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0}
dao.proposal.votes.header=Vote on all proposals dao.proposal.votes.header=Set stake for voting and publish your votes
dao.proposal.votes.header.voted=My vote dao.proposal.myVote.button=Publish votes
dao.proposal.myVote.button=Vote on all proposals dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up \
BSQ. The more BSQ you lock up, the more weight your vote will have. \n\n\
BSQ locked up for voting will be unlocked again during the vote reveal phase.
dao.proposal.create.selectProposalType=Select proposal type dao.proposal.create.selectProposalType=Select proposal type
dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.phase.inactive=Please wait until the next proposal phase
dao.proposal.create.proposalType=Proposal type dao.proposal.create.proposalType=Proposal type

View file

@ -320,6 +320,20 @@ bg color of non edit textFields: fafafa
-fx-padding: 0 10 0 10; -fx-padding: 0 10 0 10;
} }
.text-button {
-fx-background-color: transparent;
-fx-underline: true;
-fx-padding: 0 10 0 10;
-fx-pref-height: 28;
-fx-min-height: -fx-pref-height;
}
.text-button:hover {
-fx-text-fill: -bs-rd-black;
-fx-background-color: transparent;
-fx-underline: false;
}
.jfx-checkbox { .jfx-checkbox {
-jfx-checked-color: -bs-rd-green; -jfx-checked-color: -bs-rd-green;
-fx-font-size: 0.692em; -fx-font-size: 0.692em;

View file

@ -93,7 +93,7 @@ public class BondsView extends ActivatableView<GridPane, Void> {
@Override @Override
public void initialize() { public void initialize() {
tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.allBonds.header")); tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.allBonds.header"), "last");
tableView.setItems(sortedList); tableView.setItems(sortedList);
addColumns(); addColumns();

View file

@ -42,7 +42,7 @@ public class BondingDashboardView extends ActivatableView<GridPane, Void> {
public void initialize() { public void initialize() {
gridRow = bsqBalanceUtil.addGroup(root, gridRow); gridRow = bsqBalanceUtil.addGroup(root, gridRow);
gridRow = bsqBalanceUtil.addBondBalanceGroup(root, gridRow); gridRow = bsqBalanceUtil.addBondBalanceGroup(root, gridRow, "last");
} }
@Override @Override

View file

@ -144,7 +144,7 @@ public class MyReputationView extends ActivatableView<GridPane, Void> implements
lockupButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.bond.reputation.lockupButton")); lockupButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.bond.reputation.lockupButton"));
tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.reputation.table.header"), 20); tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.reputation.table.header"), 20, "last");
createColumns(); createColumns();
tableView.setItems(sortedList); tableView.setItems(sortedList);

View file

@ -93,7 +93,7 @@ public class RolesView extends ActivatableView<GridPane, Void> {
@Override @Override
public void initialize() { public void initialize() {
int gridRow = 0; int gridRow = 0;
tableView = FormBuilder.addTableViewWithHeader(root, gridRow, Res.get("dao.bond.bondedRoles")); tableView = FormBuilder.addTableViewWithHeader(root, gridRow, Res.get("dao.bond.bondedRoles"), "last");
createColumns(); createColumns();
tableView.setItems(sortedList); tableView.setItems(sortedList);

View file

@ -141,7 +141,7 @@ public class AssetFeeView extends ActivatableView<GridPane, Void> implements Bsq
payFeeButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.burnBsq.payFee")); payFeeButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.burnBsq.payFee"));
tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.burnBsq.allAssets"), 20); tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.burnBsq.allAssets"), 20, "last");
createColumns(); createColumns();
tableView.setItems(sortedList); tableView.setItems(sortedList);

View file

@ -143,7 +143,7 @@ public class ProofOfBurnView extends ActivatableView<GridPane, Void> implements
createColumnsForMyItems(); createColumnsForMyItems();
myItemsTableView.setItems(myItemsSortedList); myItemsTableView.setItems(myItemsSortedList);
allTxsTableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.proofOfBurn.allTxs"), 30); allTxsTableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.proofOfBurn.allTxs"), 30, "last");
createColumnsForAllTxs(); createColumnsForAllTxs();
allTxsTableView.setItems(allItemsSortedList); allTxsTableView.setItems(allItemsSortedList);

View file

@ -140,9 +140,10 @@ public class ProposalDisplay {
private final ChangeListener<Object> inputListener; private final ChangeListener<Object> inputListener;
private ChangeListener<Param> paramChangeListener; private ChangeListener<Param> paramChangeListener;
private ChangeListener<BondedRoleType> requiredBondForRoleListener; private ChangeListener<BondedRoleType> requiredBondForRoleListener;
private TitledGroupBg titledGroupBg; private TitledGroupBg myVoteTitledGroup;
private int titledGroupBgRowSpan; private int titledGroupBgRowSpan;
private VBox linkWithIconContainer, comboBoxValueContainer, myVoteBox, voteResultBox; private VBox linkWithIconContainer, comboBoxValueContainer, myVoteBox, voteResultBox;
private int votingBoxRowSpan;
public ProposalDisplay(GridPane gridPane, public ProposalDisplay(GridPane gridPane,
BsqFormatter bsqFormatter, BsqFormatter bsqFormatter,
@ -176,6 +177,11 @@ public class ProposalDisplay {
public void createAllFields(String title, int gridRowStartIndex, double top, ProposalType proposalType, public void createAllFields(String title, int gridRowStartIndex, double top, ProposalType proposalType,
boolean isMakeProposalScreen) { boolean isMakeProposalScreen) {
createAllFields(title, gridRowStartIndex, top, proposalType, isMakeProposalScreen, null);
}
public void createAllFields(String title, int gridRowStartIndex, double top, ProposalType proposalType,
boolean isMakeProposalScreen, String titledGroupStyle) {
removeAllFields(); removeAllFields();
this.gridRowStartIndex = gridRowStartIndex; this.gridRowStartIndex = gridRowStartIndex;
this.gridRow = gridRowStartIndex; this.gridRow = gridRowStartIndex;
@ -200,12 +206,26 @@ public class ProposalDisplay {
break; break;
} }
titledGroupBg = addTitledGroupBg(gridPane, gridRow, titledGroupBgRowSpan, title, top); TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, gridRow, titledGroupBgRowSpan, title, top);
double proposalTypeTop = top == Layout.GROUP_DISTANCE ? Layout.FIRST_ROW_AND_GROUP_DISTANCE : Layout.FIRST_ROW_DISTANCE;
if (titledGroupStyle != null) titledGroupBg.getStyleClass().add(titledGroupStyle);
double proposalTypeTop;
if (top == Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR) {
proposalTypeTop = Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE_WITHOUT_SEPARATOR;
} else if (top == Layout.GROUP_DISTANCE) {
proposalTypeTop = Layout.FIRST_ROW_AND_GROUP_DISTANCE;
} else if (top == 0) {
proposalTypeTop = Layout.FIRST_ROW_DISTANCE;
} else {
proposalTypeTop = Layout.FIRST_ROW_DISTANCE + top;
}
proposalTypeTextField = addTopLabelTextField(gridPane, gridRow, proposalTypeTextField = addTopLabelTextField(gridPane, gridRow,
Res.get("dao.proposal.display.type"), proposalType.getDisplayName(), proposalTypeTop).second; Res.get("dao.proposal.display.type"), proposalType.getDisplayName(), proposalTypeTop).second;
nameTextField = addInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name"), Layout.FIRST_ROW_DISTANCE); nameTextField = addInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name"));
if (isMakeProposalScreen) if (isMakeProposalScreen)
nameTextField.setValidator(new InputValidator()); nameTextField.setValidator(new InputValidator());
inputControls.add(nameTextField); inputControls.add(nameTextField);
@ -317,7 +337,7 @@ public class ProposalDisplay {
} }
}); });
comboBoxes.add(bondedRoleTypeComboBox); comboBoxes.add(bondedRoleTypeComboBox);
requiredBondForRoleTextField = addTopLabelTextField(gridPane, ++gridRow, requiredBondForRoleTextField = addTopLabelReadOnlyTextField(gridPane, ++gridRow,
Res.get("dao.proposal.display.requiredBondForRole.label")).second; Res.get("dao.proposal.display.requiredBondForRole.label")).second;
requiredBondForRoleListener = (observable, oldValue, newValue) -> { requiredBondForRoleListener = (observable, oldValue, newValue) -> {
@ -398,15 +418,18 @@ public class ProposalDisplay {
proposalFeeTextField.setText(bsqFormatter.formatCoinWithCode(daoFacade.getProposalFee(daoFacade.getChainHeight()))); proposalFeeTextField.setText(bsqFormatter.formatCoinWithCode(daoFacade.getProposalFee(daoFacade.getChainHeight())));
} }
Tuple3<Label, TextField, VBox> tuple3 = addTopLabelTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.myVote")); votingBoxRowSpan = 4;
myVoteTitledGroup = addTitledGroupBg(gridPane, ++gridRow, 4, Res.get("dao.proposal.myVote.title"), Layout.COMPACT_FIRST_ROW_DISTANCE);
Tuple3<Label, TextField, VBox> tuple3 = addTopLabelTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.myVote"), Layout.COMPACT_FIRST_ROW_DISTANCE);
myVoteBox = tuple3.third; myVoteBox = tuple3.third;
myVoteBox.setVisible(false); setMyVoteBoxVisibility(false);
myVoteBox.setManaged(false);
myVoteTextField = tuple3.second; myVoteTextField = tuple3.second;
tuple3 = addTopLabelTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.voteResult")); tuple3 = addTopLabelReadOnlyTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.voteResult"));
voteResultBox = tuple3.third; voteResultBox = tuple3.third;
voteResultBox.setVisible(false); voteResultBox.setVisible(false);
@ -427,12 +450,10 @@ public class ProposalDisplay {
} }
myVoteTextField.setText(myVote); myVoteTextField.setText(myVote);
myVoteBox.setVisible(isNotNull); setMyVoteBoxVisibility(isNotNull);
myVoteBox.setManaged(isNotNull);
} }
public void applyEvaluatedProposal(@Nullable EvaluatedProposal evaluatedProposal) { public void applyEvaluatedProposal(@Nullable EvaluatedProposal evaluatedProposal) {
GridPane.setRowSpan(titledGroupBg, titledGroupBgRowSpan + 1);
boolean isEvaluatedProposalNotNull = evaluatedProposal != null; boolean isEvaluatedProposalNotNull = evaluatedProposal != null;
if (isEvaluatedProposalNotNull) { if (isEvaluatedProposalNotNull) {
@ -468,11 +489,12 @@ public class ProposalDisplay {
String myVoteSummary = Res.get("dao.proposal.myVote.summary", myVote, String myVoteSummary = Res.get("dao.proposal.myVote.summary", myVote,
weight, meritString, stakeString); weight, meritString, stakeString);
myVoteTextField.setText(myVoteSummary); myVoteTextField.setText(myVoteSummary);
GridPane.setRowSpan(myVoteTitledGroup, votingBoxRowSpan - 1);
} }
boolean show = ballotIsNotNull && hasVoted; boolean show = ballotIsNotNull && hasVoted;
myVoteBox.setVisible(show); setMyVoteBoxVisibility(show);
myVoteBox.setManaged(show);
} }
public void setIsVoteIncludedInResult(boolean isVoteIncludedInResult) { public void setIsVoteIncludedInResult(boolean isVoteIncludedInResult) {
@ -658,4 +680,11 @@ public class ProposalDisplay {
return scrollPane; return scrollPane;
} }
private void setMyVoteBoxVisibility(boolean visibility) {
myVoteTitledGroup.setVisible(visibility);
myVoteTitledGroup.setManaged(visibility);
myVoteBox.setVisible(visibility);
myVoteBox.setManaged(visibility);
}
} }

View file

@ -19,6 +19,7 @@ package bisq.desktop.main.dao.governance.dashboard;
import bisq.desktop.common.view.ActivatableView; import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView; import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.dao.governance.PhasesView; import bisq.desktop.main.dao.governance.PhasesView;
import bisq.desktop.util.Layout; import bisq.desktop.util.Layout;
@ -69,7 +70,8 @@ public class GovernanceDashboardView extends ActivatableView<GridPane, Void> imp
public void initialize() { public void initialize() {
gridRow = phasesView.addGroup(root, gridRow); gridRow = phasesView.addGroup(root, gridRow);
addTitledGroupBg(root, ++gridRow, 6, Res.get("dao.cycle.overview.headline"), Layout.GROUP_DISTANCE); TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 6, Res.get("dao.cycle.overview.headline"), Layout.GROUP_DISTANCE);
titledGroupBg.getStyleClass().add("last");
currentBlockHeightTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.cycle.currentBlockHeight"), currentBlockHeightTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.cycle.currentBlockHeight"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
currentPhaseTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.cycle.currentPhase")).second; currentPhaseTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.cycle.currentPhase")).second;

View file

@ -161,6 +161,7 @@ public class MakeProposalView extends ActivatableView<GridPane, Void> implements
gridRow = phasesView.addGroup(root, gridRow); gridRow = phasesView.addGroup(root, gridRow);
proposalTitledGroup = addTitledGroupBg(root, ++gridRow, 2, proposalGroupTitle.get(), Layout.GROUP_DISTANCE); proposalTitledGroup = addTitledGroupBg(root, ++gridRow, 2, proposalGroupTitle.get(), Layout.GROUP_DISTANCE);
proposalTitledGroup.getStyleClass().add("last");
final Tuple3<Label, TextField, VBox> nextProposalPhaseTuple = addTopLabelReadOnlyTextField(root, gridRow, final Tuple3<Label, TextField, VBox> nextProposalPhaseTuple = addTopLabelReadOnlyTextField(root, gridRow,
Res.get("dao.cycle.proposal.next"), Res.get("dao.cycle.proposal.next"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE); Layout.FIRST_ROW_AND_GROUP_DISTANCE);
@ -444,7 +445,7 @@ public class MakeProposalView extends ActivatableView<GridPane, Void> implements
if (selectedProposalType != null) { if (selectedProposalType != null) {
proposalDisplay = new ProposalDisplay(root, bsqFormatter, daoFacade, changeParamValidator, navigation, null); proposalDisplay = new ProposalDisplay(root, bsqFormatter, daoFacade, changeParamValidator, navigation, null);
proposalDisplay.createAllFields(Res.get("dao.proposal.create.new"), alwaysVisibleGridRowIndex, Layout.GROUP_DISTANCE, proposalDisplay.createAllFields(Res.get("dao.proposal.create.new"), alwaysVisibleGridRowIndex, Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR,
selectedProposalType, true); selectedProposalType, true);
final Tuple4<Button, BusyAnimation, Label, HBox> makeProposalTuple = addButtonBusyAnimationLabelAfterGroup(root, final Tuple4<Button, BusyAnimation, Label, HBox> makeProposalTuple = addButtonBusyAnimationLabelAfterGroup(root,

View file

@ -17,7 +17,6 @@
package bisq.desktop.main.dao.governance.proposals; package bisq.desktop.main.dao.governance.proposals;
import bisq.desktop.Navigation;
import bisq.desktop.common.view.ActivatableView; import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView; import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipLabel; import bisq.desktop.components.AutoTooltipLabel;
@ -29,8 +28,8 @@ import bisq.desktop.components.TableGroupHeadline;
import bisq.desktop.components.TitledGroupBg; import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.components.TxIdTextField; import bisq.desktop.components.TxIdTextField;
import bisq.desktop.main.dao.governance.PhasesView; import bisq.desktop.main.dao.governance.PhasesView;
import bisq.desktop.main.dao.governance.ProposalDisplay;
import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.SelectProposalWindow;
import bisq.desktop.util.GUIUtil; import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout; import bisq.desktop.util.Layout;
import bisq.desktop.util.validation.BsqValidator; import bisq.desktop.util.validation.BsqValidator;
@ -42,7 +41,6 @@ import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.DaoFacade; import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.blindvote.BlindVoteConsensus; import bisq.core.dao.governance.blindvote.BlindVoteConsensus;
import bisq.core.dao.governance.myvote.MyVote; import bisq.core.dao.governance.myvote.MyVote;
import bisq.core.dao.governance.proposal.param.ChangeParamValidator;
import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.Block;
@ -71,7 +69,6 @@ import com.jfoenix.controls.JFXButton;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableCell; import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
@ -101,11 +98,13 @@ import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static bisq.desktop.util.FormBuilder.*; import static bisq.desktop.util.FormBuilder.*;
import static bisq.desktop.util.Layout.INITIAL_WINDOW_HEIGHT;
@FxmlView @FxmlView
public class ProposalsView extends ActivatableView<GridPane, Void> implements BsqBalanceListener, DaoStateListener { public class ProposalsView extends ActivatableView<GridPane, Void> implements BsqBalanceListener, DaoStateListener {
@ -113,11 +112,10 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private final BsqWalletService bsqWalletService; private final BsqWalletService bsqWalletService;
private final PhasesView phasesView; private final PhasesView phasesView;
private final DaoStateService daoStateService; private final DaoStateService daoStateService;
private final ChangeParamValidator changeParamValidator;
private final Preferences preferences; private final Preferences preferences;
private final BsqFormatter bsqFormatter; private final BsqFormatter bsqFormatter;
private final BSFormatter btcFormatter; private final BSFormatter btcFormatter;
private final Navigation navigation; private final SelectProposalWindow selectProposalWindow;
private final ObservableList<ProposalsListItem> listItems = FXCollections.observableArrayList(); private final ObservableList<ProposalsListItem> listItems = FXCollections.observableArrayList();
private final SortedList<ProposalsListItem> sortedList = new SortedList<>(listItems); private final SortedList<ProposalsListItem> sortedList = new SortedList<>(listItems);
@ -125,20 +123,15 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private final List<Node> voteFields = new ArrayList<>(); private final List<Node> voteFields = new ArrayList<>();
private TableView<ProposalsListItem> tableView; private TableView<ProposalsListItem> tableView;
private TitledGroupBg voteTitledGroupBg;
private Label voteButtonInfoLabel; private Label voteButtonInfoLabel;
private TxIdTextField revealTxIdTextField, blindVoteTxIdTextField; private TxIdTextField revealTxIdTextField, blindVoteTxIdTextField;
private TextField meritTextField; private TextField meritTextField;
private VBox blindVoteTxIdContainer, revealTxIdContainer; private VBox blindVoteTxIdContainer, revealTxIdContainer;
private Button removeProposalButton, acceptButton, rejectButton, ignoreButton, voteButton; private Button voteButton;
private InputTextField stakeInputTextField; private InputTextField stakeInputTextField;
private ScrollPane proposalDisplayView;
private GridPane proposalDisplayGridPane;
private BusyAnimation voteButtonBusyAnimation; private BusyAnimation voteButtonBusyAnimation;
private ProposalDisplay proposalDisplay;
private int gridRow = 0; private int gridRow = 0;
private boolean proposalDisplayInitialized;
private ProposalsListItem selectedItem; private ProposalsListItem selectedItem;
private DaoPhase.Phase currentPhase; private DaoPhase.Phase currentPhase;
private ListChangeListener<Proposal> proposalListChangeListener; private ListChangeListener<Proposal> proposalListChangeListener;
@ -147,6 +140,16 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private Subscription selectedProposalSubscription, phaseSubscription; private Subscription selectedProposalSubscription, phaseSubscription;
private boolean areVoteButtonsVisible; private boolean areVoteButtonsVisible;
private TableColumn<ProposalsListItem, ProposalsListItem> lastColumn; private TableColumn<ProposalsListItem, ProposalsListItem> lastColumn;
private String shownVoteOnProposalWindowForTxId = "";
private final double initialProposalTableViewHeight = 180;
private final double pixelsPerProposalTableRow = (initialProposalTableViewHeight - 28) / 4.0;
private final Function<Double, Double> proposalTableViewHeight = (screenSize) -> {
int extraRows = screenSize <= INITIAL_WINDOW_HEIGHT ? 0 : (int) ((screenSize - INITIAL_WINDOW_HEIGHT) / pixelsPerProposalTableRow);
return extraRows == 0 ? initialProposalTableViewHeight : Math.ceil(initialProposalTableViewHeight + (extraRows * pixelsPerProposalTableRow));
};
private ChangeListener<Number> bisqWindowVerticalSizeListener;
private TableGroupHeadline proposalsHeadline;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -158,20 +161,18 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
BsqWalletService bsqWalletService, BsqWalletService bsqWalletService,
PhasesView phasesView, PhasesView phasesView,
DaoStateService daoStateService, DaoStateService daoStateService,
ChangeParamValidator changeParamValidator,
Preferences preferences, Preferences preferences,
BsqFormatter bsqFormatter, BsqFormatter bsqFormatter,
BSFormatter btcFormatter, BSFormatter btcFormatter,
Navigation navigation) { SelectProposalWindow selectProposalWindow) {
this.daoFacade = daoFacade; this.daoFacade = daoFacade;
this.bsqWalletService = bsqWalletService; this.bsqWalletService = bsqWalletService;
this.phasesView = phasesView; this.phasesView = phasesView;
this.daoStateService = daoStateService; this.daoStateService = daoStateService;
this.changeParamValidator = changeParamValidator;
this.preferences = preferences; this.preferences = preferences;
this.bsqFormatter = bsqFormatter; this.bsqFormatter = bsqFormatter;
this.btcFormatter = btcFormatter; this.btcFormatter = btcFormatter;
this.navigation = navigation; this.selectProposalWindow = selectProposalWindow;
} }
@Override @Override
@ -180,15 +181,21 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
gridRow = phasesView.addGroup(root, gridRow); gridRow = phasesView.addGroup(root, gridRow);
proposalDisplayGridPane = new GridPane();
createProposalsTableView(); createProposalsTableView();
createEmptyProposalDisplay();
createVoteView(); createVoteView();
ballotListChangeListener = c -> updateListItems(); ballotListChangeListener = c -> updateListItems();
proposalListChangeListener = c -> updateListItems(); proposalListChangeListener = c -> updateListItems();
bisqWindowVerticalSizeListener = (observable, oldValue, newValue) -> {
double newTableViewHeight = proposalTableViewHeight.apply(newValue.doubleValue());
if (tableView.getHeight() != newTableViewHeight) {
tableView.setMinHeight(newTableViewHeight);
double diff = newTableViewHeight - tableView.getHeight();
proposalsHeadline.setMaxHeight(proposalsHeadline.getHeight() + diff);
}
};
stakeListener = (observable, oldValue, newValue) -> updateViews(); stakeListener = (observable, oldValue, newValue) -> updateViews();
} }
@ -218,8 +225,10 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
bsqWalletService.getUnlockingBondsBalance()); bsqWalletService.getUnlockingBondsBalance());
updateListItems(); updateListItems();
GUIUtil.setFitToRowsForTableView(tableView, 38, 28, 2, 6); GUIUtil.setFitToRowsForTableView(tableView, 38, 28, 4, 4);
updateViews(); updateViews();
root.getScene().heightProperty().addListener(bisqWindowVerticalSizeListener);
} }
@Override @Override
@ -242,8 +251,6 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
} }
if (voteButton != null) if (voteButton != null)
voteButton.setOnAction(null); voteButton.setOnAction(null);
if (removeProposalButton != null)
removeProposalButton.setOnAction(null);
listItems.forEach(ProposalsListItem::cleanup); listItems.forEach(ProposalsListItem::cleanup);
tableView.getSelectionModel().clearSelection(); tableView.getSelectionModel().clearSelection();
@ -316,89 +323,39 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
listItems.clear(); listItems.clear();
fillListItems(); fillListItems();
if (listItems.isEmpty())
hideProposalDisplay();
if (!tableView.getItems().isEmpty()) {
onSelectProposal(tableView.getItems().get(0));
onSelectProposal(null);
}
GUIUtil.setFitToRowsForTableView(tableView, 38, 28, 2, 6);
} }
private void createAllFieldsOnProposalDisplay(Proposal proposal, @Nullable Ballot ballot, private void showVoteOnProposalWindow(Proposal proposal, @Nullable Ballot ballot,
@Nullable EvaluatedProposal evaluatedProposal) { @Nullable EvaluatedProposal evaluatedProposal) {
proposalDisplayView.setVisible(true);
proposalDisplayView.setManaged(true);
proposalDisplay.createAllFields(Res.get("dao.proposal.selectedProposal"), 0, 0, proposal.getType(), if (!shownVoteOnProposalWindowForTxId.equals(proposal.getTxId())) {
false); shownVoteOnProposalWindowForTxId = proposal.getTxId();
proposalDisplay.setEditable(false);
proposalDisplay.applyProposalPayload(proposal); selectProposalWindow.show(proposal, evaluatedProposal, ballot);
selectProposalWindow.onAccept(() -> {
shownVoteOnProposalWindowForTxId = "";
onAccept();
});
selectProposalWindow.onReject(() -> {
shownVoteOnProposalWindowForTxId = "";
onReject();
});
selectProposalWindow.onIgnore(() -> {
shownVoteOnProposalWindowForTxId = "";
onIgnore();
});
selectProposalWindow.onRemove(() -> {
shownVoteOnProposalWindowForTxId = "";
onRemoveProposal();
});
proposalDisplay.applyEvaluatedProposal(evaluatedProposal); selectProposalWindow.onClose(() -> {
shownVoteOnProposalWindowForTxId = "";
Tuple2<Long, Long> meritAndStakeTuple = daoFacade.getMeritAndStakeForProposal(proposal.getTxId()); tableView.getSelectionModel().clearSelection();
long merit = meritAndStakeTuple.first; });
long stake = meritAndStakeTuple.second;
proposalDisplay.applyBallotAndVoteWeight(ballot, merit, stake);
proposalDisplayInitialized = true;
removeProposalButton = addButtonAfterGroup(proposalDisplayGridPane, proposalDisplay.incrementAndGetGridRow(), Res.get("shared.remove"));
removeProposalButton.setOnAction(event -> onRemoveProposal());
onPhaseChanged(daoFacade.phaseProperty().get());
Tuple3<Button, Button, Button> tuple = add3ButtonsAfterGroup(proposalDisplayGridPane,
proposalDisplay.incrementAndGetGridRow(),
Res.get("dao.proposal.myVote.accept"),
Res.get("dao.proposal.myVote.reject"),
Res.get("dao.proposal.myVote.removeMyVote"));
acceptButton = tuple.first;
acceptButton.setDefaultButton(false);
rejectButton = tuple.second;
ignoreButton = tuple.third;
acceptButton.setOnAction(event -> onAccept());
rejectButton.setOnAction(event -> onReject());
ignoreButton.setOnAction(event -> onIgnore());
voteButtons.clear();
voteButtons.add(voteButton);
voteButtons.add(acceptButton);
voteButtons.add(rejectButton);
voteButtons.add(ignoreButton);
}
private void hideProposalDisplay() {
if (proposalDisplayInitialized) {
proposalDisplay.removeAllFields();
proposalDisplayView.setVisible(false);
proposalDisplayView.setManaged(false);
}
if (removeProposalButton != null) {
removeProposalButton.setManaged(false);
removeProposalButton.setVisible(false);
}
if (acceptButton != null) {
acceptButton.setManaged(false);
acceptButton.setVisible(false);
}
if (rejectButton != null) {
rejectButton.setManaged(false);
rejectButton.setVisible(false);
}
if (ignoreButton != null) {
ignoreButton.setManaged(false);
ignoreButton.setVisible(false);
} }
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Handlers // Handlers
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -407,15 +364,6 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
if (phase != null && !phase.equals(currentPhase)) { if (phase != null && !phase.equals(currentPhase)) {
currentPhase = phase; currentPhase = phase;
stakeInputTextField.clear(); stakeInputTextField.clear();
onSelectProposal(selectedItem);
}
if (removeProposalButton != null) {
boolean doShowRemoveButton = phase == DaoPhase.Phase.PROPOSAL &&
selectedItem != null &&
daoFacade.isMyProposal(selectedItem.getProposal());
removeProposalButton.setVisible(doShowRemoveButton);
removeProposalButton.setManaged(doShowRemoveButton);
} }
updateViews(); updateViews();
@ -427,9 +375,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
new Popup<>().warning(Res.get("dao.proposal.active.remove.confirm")) new Popup<>().warning(Res.get("dao.proposal.active.remove.confirm"))
.actionButtonText(Res.get("dao.proposal.active.remove.doRemove")) .actionButtonText(Res.get("dao.proposal.active.remove.doRemove"))
.onAction(() -> { .onAction(() -> {
if (daoFacade.removeMyProposal(proposal)) { if (!daoFacade.removeMyProposal(proposal)) {
hideProposalDisplay();
} else {
new Popup<>().warning(Res.get("dao.proposal.active.remove.failed")).show(); new Popup<>().warning(Res.get("dao.proposal.active.remove.failed")).show();
} }
tableView.getSelectionModel().clearSelection(); tableView.getSelectionModel().clearSelection();
@ -448,14 +394,11 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
.findAny() .findAny()
.orElse(null); .orElse(null);
createAllFieldsOnProposalDisplay(selectedItem.getProposal(), selectedItem.getBallot(), evaluatedProposal);
applyMerit(); applyMerit();
} else { showVoteOnProposalWindow(selectedItem.getProposal(), selectedItem.getBallot(), evaluatedProposal);
hideProposalDisplay();
} }
onPhaseChanged(daoFacade.phaseProperty().get());
updateViews(); onPhaseChanged(daoFacade.phaseProperty().get());
} }
private void applyMerit() { private void applyMerit() {
@ -479,20 +422,31 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private void onAccept() { private void onAccept() {
daoFacade.setVote(getBallotListItem().getBallot(), new Vote(true)); daoFacade.setVote(getBallotListItem().getBallot(), new Vote(true));
proposalDisplay.applyBallot(getBallotListItem().getBallot());
updateStateAfterVote(); updateStateAfterVote();
tableView.getSelectionModel().clearSelection();
showHowToSetStakeForVotingPopup();
}
private void showHowToSetStakeForVotingPopup() {
String id = "explainHowToSetStakeForVoting";
if (preferences.showAgain(id))
new Popup<>().information(Res.get("dao.proposal.myVote.setStake.description"))
.dontShowAgainId(id).show();
} }
private void onReject() { private void onReject() {
daoFacade.setVote(getBallotListItem().getBallot(), new Vote(false)); daoFacade.setVote(getBallotListItem().getBallot(), new Vote(false));
proposalDisplay.applyBallot(getBallotListItem().getBallot());
updateStateAfterVote(); updateStateAfterVote();
tableView.getSelectionModel().clearSelection();
showHowToSetStakeForVotingPopup();
} }
private void onIgnore() { private void onIgnore() {
daoFacade.setVote(getBallotListItem().getBallot(), null); daoFacade.setVote(getBallotListItem().getBallot(), null);
proposalDisplay.applyBallot(getBallotListItem().getBallot());
updateStateAfterVote(); updateStateAfterVote();
tableView.getSelectionModel().clearSelection();
showHowToSetStakeForVotingPopup();
} }
private void onVote() { private void onVote() {
@ -545,13 +499,6 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
return selectedItem; return selectedItem;
} }
private Optional<Vote> getVote(@Nullable Ballot ballot) {
if (ballot == null)
return Optional.empty();
else
return ballot.getVoteAsOptional();
}
private void updateViews() { private void updateViews() {
boolean isBlindVotePhaseButNotLastBlock = isBlindVotePhaseButNotLastBlock(); boolean isBlindVotePhaseButNotLastBlock = isBlindVotePhaseButNotLastBlock();
boolean hasVotedOnProposal = hasVotedOnProposal(); boolean hasVotedOnProposal = hasVotedOnProposal();
@ -560,17 +507,8 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
List<MyVote> myVoteListForCycle = daoFacade.getMyVoteListForCycle(); List<MyVote> myVoteListForCycle = daoFacade.getMyVoteListForCycle();
boolean hasAlreadyVoted = !myVoteListForCycle.isEmpty(); boolean hasAlreadyVoted = !myVoteListForCycle.isEmpty();
if (selectedItem != null && acceptButton != null) { if (selectedItem != null) {
Optional<Vote> optionalVote = getVote(selectedItem.getBallot());
boolean isPresent = optionalVote.isPresent();
boolean isAccepted = isPresent && optionalVote.get().isAccepted();
acceptButton.setDisable((isPresent && isAccepted));
rejectButton.setDisable((isPresent && !isAccepted));
ignoreButton.setDisable(!isPresent);
stakeInputTextField.setMouseTransparent(hasAlreadyVoted || !isBlindVotePhaseButNotLastBlock); stakeInputTextField.setMouseTransparent(hasAlreadyVoted || !isBlindVotePhaseButNotLastBlock);
} else {
stakeInputTextField.setMouseTransparent(true);
} }
boolean hasProposals = !daoFacade.getActiveOrMyUnconfirmedProposals().isEmpty(); boolean hasProposals = !daoFacade.getActiveOrMyUnconfirmedProposals().isEmpty();
@ -595,7 +533,6 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
revealTxIdContainer.setManaged(false); revealTxIdContainer.setManaged(false);
if (hasAlreadyVoted) { if (hasAlreadyVoted) {
voteTitledGroupBg.setText(Res.get("dao.proposal.votes.header.voted"));
if (myVoteListForCycle.size() == 1) { if (myVoteListForCycle.size() == 1) {
Optional<MyVote> optionalMyVote = myVoteListForCycle.stream() Optional<MyVote> optionalMyVote = myVoteListForCycle.stream()
.filter(myVote -> daoFacade.isTxInCorrectCycle(myVote.getHeight(), daoFacade.getChainHeight())) .filter(myVote -> daoFacade.isTxInCorrectCycle(myVote.getHeight(), daoFacade.getChainHeight()))
@ -660,7 +597,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void createProposalsTableView() { private void createProposalsTableView() {
TableGroupHeadline proposalsHeadline = new TableGroupHeadline(Res.get("dao.proposal.active.header")); proposalsHeadline = new TableGroupHeadline(Res.get("dao.proposal.active.header"));
GridPane.setRowIndex(proposalsHeadline, ++gridRow); GridPane.setRowIndex(proposalsHeadline, ++gridRow);
GridPane.setMargin(proposalsHeadline, new Insets(Layout.GROUP_DISTANCE, -10, -10, -10)); GridPane.setMargin(proposalsHeadline, new Insets(Layout.GROUP_DISTANCE, -10, -10, -10));
root.getChildren().add(proposalsHeadline); root.getChildren().add(proposalsHeadline);
@ -672,25 +609,17 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
createProposalColumns(); createProposalColumns();
GridPane.setRowIndex(tableView, gridRow); GridPane.setRowIndex(tableView, gridRow);
GridPane.setHgrow(tableView, Priority.ALWAYS); GridPane.setHgrow(tableView, Priority.ALWAYS);
GridPane.setVgrow(tableView, Priority.SOMETIMES);
GridPane.setMargin(tableView, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, -10, 5, -10)); GridPane.setMargin(tableView, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, -10, 5, -10));
root.getChildren().add(tableView); root.getChildren().add(tableView);
tableView.setItems(sortedList); tableView.setItems(sortedList);
} }
private void createEmptyProposalDisplay() {
proposalDisplay = new ProposalDisplay(proposalDisplayGridPane, bsqFormatter, daoFacade,
changeParamValidator, navigation, preferences);
proposalDisplayView = proposalDisplay.getView();
GridPane.setMargin(proposalDisplayView, new Insets(0, -10, 0, -10));
GridPane.setRowIndex(proposalDisplayView, ++gridRow);
GridPane.setHgrow(proposalDisplayView, Priority.ALWAYS);
root.getChildren().add(proposalDisplayView);
}
private void createVoteView() { private void createVoteView() {
voteTitledGroupBg = addTitledGroupBg(root, ++gridRow, 4, TitledGroupBg voteTitledGroupBg = addTitledGroupBg(root, ++gridRow, 4,
Res.get("dao.proposal.votes.header"), 20); Res.get("dao.proposal.votes.header"), 20);
voteTitledGroupBg.getStyleClass().add("last");
voteFields.add(voteTitledGroupBg); voteFields.add(voteTitledGroupBg);
Tuple3<Label, TextField, VBox> meritTuple = addTopLabelTextField(root, gridRow, Tuple3<Label, TextField, VBox> meritTuple = addTopLabelTextField(root, gridRow,
@ -866,7 +795,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
JFXButton iconButton = item.getIconButton(); JFXButton iconButton = item.getIconButton();
if (iconButton != null) { if (iconButton != null) {
iconButton.setOnAction(e -> { iconButton.setOnAction(e -> {
onSelectProposal(item); selectedItem = item;
if (areVoteButtonsVisible) { if (areVoteButtonsVisible) {
if (iconButton.getUserData() == ProposalsListItem.IconButtonTypes.ACCEPT) if (iconButton.getUserData() == ProposalsListItem.IconButtonTypes.ACCEPT)
onReject(); onReject();

View file

@ -17,7 +17,6 @@
package bisq.desktop.main.dao.governance.result; package bisq.desktop.main.dao.governance.result;
import bisq.desktop.Navigation;
import bisq.desktop.common.view.ActivatableView; import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView; import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipLabel; import bisq.desktop.components.AutoTooltipLabel;
@ -25,9 +24,9 @@ import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.TableGroupHeadline; import bisq.desktop.components.TableGroupHeadline;
import bisq.desktop.main.dao.governance.PhasesView; import bisq.desktop.main.dao.governance.PhasesView;
import bisq.desktop.main.dao.governance.ProposalDisplay;
import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.DaoTestingFeedbackWindow; import bisq.desktop.main.overlays.windows.DaoTestingFeedbackWindow;
import bisq.desktop.main.overlays.windows.ProposalResultsWindow;
import bisq.desktop.util.FormBuilder; import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil; import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout; import bisq.desktop.util.Layout;
@ -63,11 +62,9 @@ import bisq.core.dao.state.model.governance.RoleProposal;
import bisq.core.dao.state.model.governance.Vote; import bisq.core.dao.state.model.governance.Vote;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.user.DontShowAgainLookup; import bisq.core.user.DontShowAgainLookup;
import bisq.core.user.Preferences;
import bisq.core.util.BsqFormatter; import bisq.core.util.BsqFormatter;
import bisq.common.UserThread; import bisq.common.UserThread;
import bisq.common.util.Tuple2;
import bisq.common.util.Utilities; import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@ -79,21 +76,18 @@ import com.google.gson.JsonObject;
import javax.inject.Inject; import javax.inject.Inject;
import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableCell; import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.geometry.HPos; import javafx.geometry.HPos;
import javafx.geometry.Insets; import javafx.geometry.Insets;
@ -118,6 +112,8 @@ import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static bisq.desktop.util.FormBuilder.addButton;
@FxmlView @FxmlView
public class VoteResultView extends ActivatableView<GridPane, Void> implements DaoStateListener { public class VoteResultView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private final DaoFacade daoFacade; private final DaoFacade daoFacade;
@ -128,11 +124,10 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
private final ProposalService proposalService; private final ProposalService proposalService;
private final PeriodService periodService; private final PeriodService periodService;
private final BsqWalletService bsqWalletService; private final BsqWalletService bsqWalletService;
private final Preferences preferences;
private final BsqFormatter bsqFormatter; private final BsqFormatter bsqFormatter;
private final Navigation navigation;
private final MyProposalListService myProposalListService; private final MyProposalListService myProposalListService;
private final MyBlindVoteListService myBlindVoteListService; private final MyBlindVoteListService myBlindVoteListService;
private ProposalResultsWindow proposalResultsWindow;
private Button exportButton; private Button exportButton;
private int gridRow = 0; private int gridRow = 0;
@ -152,7 +147,6 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
private ChangeListener<CycleListItem> selectedVoteResultListItemListener; private ChangeListener<CycleListItem> selectedVoteResultListItemListener;
private ResultsOfCycle resultsOfCycle; private ResultsOfCycle resultsOfCycle;
private ProposalListItem selectedProposalListItem; private ProposalListItem selectedProposalListItem;
private TableView<VoteListItem> votesTableView;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -168,11 +162,10 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
ProposalService proposalService, ProposalService proposalService,
PeriodService periodService, PeriodService periodService,
BsqWalletService bsqWalletService, BsqWalletService bsqWalletService,
Preferences preferences,
BsqFormatter bsqFormatter, BsqFormatter bsqFormatter,
Navigation navigation,
MyProposalListService myProposalListService, MyProposalListService myProposalListService,
MyBlindVoteListService myBlindVoteListService) { MyBlindVoteListService myBlindVoteListService,
ProposalResultsWindow proposalResultsWindow) {
this.daoFacade = daoFacade; this.daoFacade = daoFacade;
this.phasesView = phasesView; this.phasesView = phasesView;
this.daoStateService = daoStateService; this.daoStateService = daoStateService;
@ -181,11 +174,10 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
this.proposalService = proposalService; this.proposalService = proposalService;
this.periodService = periodService; this.periodService = periodService;
this.bsqWalletService = bsqWalletService; this.bsqWalletService = bsqWalletService;
this.preferences = preferences;
this.bsqFormatter = bsqFormatter; this.bsqFormatter = bsqFormatter;
this.navigation = navigation;
this.myProposalListService = myProposalListService; this.myProposalListService = myProposalListService;
this.myBlindVoteListService = myBlindVoteListService; this.myBlindVoteListService = myBlindVoteListService;
this.proposalResultsWindow = proposalResultsWindow;
} }
@Override @Override
@ -195,8 +187,9 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
selectedVoteResultListItemListener = (observable, oldValue, newValue) -> onResultsListItemSelected(newValue); selectedVoteResultListItemListener = (observable, oldValue, newValue) -> onResultsListItemSelected(newValue);
createCyclesTable(); createCyclesTable();
exportButton = FormBuilder.addButton(root, ++gridRow, Res.get("shared.exportJSON")); exportButton = addButton(root, ++gridRow, Res.get("shared.exportJSON"));
GridPane.setMargin(exportButton, new Insets(20, -10, -40, 0)); exportButton.getStyleClass().add("text-button");
GridPane.setMargin(exportButton, new Insets(10, -10, -50, 0));
GridPane.setColumnSpan(exportButton, 2); GridPane.setColumnSpan(exportButton, 2);
GridPane.setHalignment(exportButton, HPos.RIGHT); GridPane.setHalignment(exportButton, HPos.RIGHT);
} }
@ -216,12 +209,9 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
GUIUtil.exportJSON("voteResultsHistory.json", cyclesJsonArray, (Stage) root.getScene().getWindow()); GUIUtil.exportJSON("voteResultsHistory.json", cyclesJsonArray, (Stage) root.getScene().getWindow());
}); });
if (proposalsTableView != null) { if (proposalsTableView != null) {
GUIUtil.setFitToRowsForTableView(proposalsTableView, 25, 28, 2, 4); GUIUtil.setFitToRowsForTableView(proposalsTableView, 25, 28, 6, 6);
} }
if (votesTableView != null) { GUIUtil.setFitToRowsForTableView(cyclesTableView, 25, 28, 6, 6);
GUIUtil.setFitToRowsForTableView(votesTableView, 25, 28, 2, 4);
}
GUIUtil.setFitToRowsForTableView(cyclesTableView, 25, 28, 2, 4);
} }
@Override @Override
@ -336,17 +326,30 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
Optional<Ballot> optionalBallot = daoFacade.getAllValidBallots().stream() Optional<Ballot> optionalBallot = daoFacade.getAllValidBallots().stream()
.filter(ballot -> ballot.getTxId().equals(evaluatedProposal.getProposalTxId())) .filter(ballot -> ballot.getTxId().equals(evaluatedProposal.getProposalTxId()))
.findAny(); .findAny();
Ballot ballot = optionalBallot.orElse(null);
ProposalDisplay proposalDisplay = createProposalDisplay(evaluatedProposal, ballot);
createVotesTable();
Ballot ballot = optionalBallot.orElse(null);
// Check if my vote is included in result // Check if my vote is included in result
boolean isVoteIncludedInResult = voteListItemList.stream() boolean isVoteIncludedInResult = voteListItemList.stream()
.anyMatch(voteListItem -> bsqWalletService.getTransaction(voteListItem.getBlindVoteTxId()) != null); .anyMatch(voteListItem -> bsqWalletService.getTransaction(voteListItem.getBlindVoteTxId()) != null);
proposalDisplay.setIsVoteIncludedInResult(isVoteIncludedInResult);
voteListItemList.clear();
resultsOfCycle.getEvaluatedProposals().stream()
.filter(evProposal -> evProposal.getProposal().equals(selectedProposalListItem.getEvaluatedProposal().getProposal()))
.forEach(evProposal -> resultsOfCycle.getDecryptedVotesForCycle().forEach(decryptedBallotsWithMerits ->
voteListItemList.add(new VoteListItem(evProposal.getProposal(), decryptedBallotsWithMerits,
daoStateService, bsqFormatter))));
voteListItemList.sort(Comparator.comparing(VoteListItem::getBlindVoteTxId));
showProposalResultWindow(evaluatedProposal, ballot, isVoteIncludedInResult, sortedVoteListItemList);
} }
} }
private void showProposalResultWindow(EvaluatedProposal evaluatedProposal, Ballot ballot,
boolean isVoteIncludedInResult, SortedList<VoteListItem> sortedVoteListItemList) {
proposalResultsWindow.show(evaluatedProposal, ballot, isVoteIncludedInResult, sortedVoteListItemList);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Fill lists: Cycle // Fill lists: Cycle
@ -386,7 +389,7 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
maybeShowDaoTestingFeedbackWindow(); maybeShowDaoTestingFeedbackWindow();
GUIUtil.setFitToRowsForTableView(cyclesTableView, 25, 28, 2, 4); GUIUtil.setFitToRowsForTableView(cyclesTableView, 25, 28, 6, 6);
} }
private void maybeShowDaoTestingFeedbackWindow() { private void maybeShowDaoTestingFeedbackWindow() {
@ -450,7 +453,7 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
createProposalsColumns(proposalsTableView); createProposalsColumns(proposalsTableView);
GridPane.setRowIndex(proposalsTableView, gridRow); GridPane.setRowIndex(proposalsTableView, gridRow);
GridPane.setMargin(proposalsTableView, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, -10, 5, -10)); GridPane.setMargin(proposalsTableView, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, -10, 0, -10));
GridPane.setColumnSpan(proposalsTableView, 2); GridPane.setColumnSpan(proposalsTableView, 2);
root.getChildren().add(proposalsTableView); root.getChildren().add(proposalsTableView);
@ -477,81 +480,9 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
ballotByProposalTxIdMap.get(evaluatedProposal.getProposalTxId()), ballotByProposalTxIdMap.get(evaluatedProposal.getProposalTxId()),
bsqFormatter)) bsqFormatter))
.collect(Collectors.toList())); .collect(Collectors.toList()));
GUIUtil.setFitToRowsForTableView(proposalsTableView, 25, 28, 2, 4); GUIUtil.setFitToRowsForTableView(proposalsTableView, 25, 28, 6, 6);
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Create views: proposalDisplay
///////////////////////////////////////////////////////////////////////////////////////////
private ProposalDisplay createProposalDisplay(EvaluatedProposal evaluatedProposal, Ballot ballot) {
Proposal proposal = evaluatedProposal.getProposal();
ProposalDisplay proposalDisplay = new ProposalDisplay(new GridPane(), bsqFormatter,
daoFacade, null, navigation, preferences);
ScrollPane proposalDisplayView = proposalDisplay.getView();
GridPane.setMargin(proposalDisplayView, new Insets(0, -10, -15, -10));
GridPane.setRowIndex(proposalDisplayView, ++gridRow);
GridPane.setColumnSpan(proposalDisplayView, 2);
GridPane.setHgrow(proposalDisplayView, Priority.ALWAYS);
root.getChildren().add(proposalDisplayView);
proposalDisplay.createAllFields(Res.get("dao.proposal.selectedProposal"), 0, 0,
proposal.getType(), false);
proposalDisplay.setEditable(false);
proposalDisplay.applyProposalPayload(proposal);
proposalDisplay.applyEvaluatedProposal(evaluatedProposal);
Tuple2<Long, Long> meritAndStakeTuple = daoFacade.getMeritAndStakeForProposal(proposal.getTxId());
long merit = meritAndStakeTuple.first;
long stake = meritAndStakeTuple.second;
proposalDisplay.applyBallotAndVoteWeight(ballot, merit, stake);
return proposalDisplay;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Create views: votesTableView
///////////////////////////////////////////////////////////////////////////////////////////
private void createVotesTable() {
TableGroupHeadline votesTableHeader = new TableGroupHeadline(Res.get("dao.results.proposals.voting.detail.header"));
GridPane.setRowIndex(votesTableHeader, ++gridRow);
GridPane.setMargin(votesTableHeader, new Insets(Layout.GROUP_DISTANCE, -10, -10, -10));
GridPane.setColumnSpan(votesTableHeader, 2);
root.getChildren().add(votesTableHeader);
votesTableView = new TableView<>();
votesTableView.setPlaceholder(new AutoTooltipLabel(Res.get("table.placeholder.noData")));
votesTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
createColumns(votesTableView);
GridPane.setRowIndex(votesTableView, gridRow);
GridPane.setMargin(votesTableView, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, -10, -15, -10));
GridPane.setColumnSpan(votesTableView, 2);
root.getChildren().add(votesTableView);
votesTableView.setItems(sortedVoteListItemList);
sortedVoteListItemList.comparatorProperty().bind(votesTableView.comparatorProperty());
voteListItemList.clear();
resultsOfCycle.getEvaluatedProposals().stream()
.filter(evaluatedProposal -> evaluatedProposal.getProposal().equals(selectedProposalListItem.getEvaluatedProposal().getProposal()))
.forEach(evaluatedProposal -> {
resultsOfCycle.getDecryptedVotesForCycle().forEach(decryptedBallotsWithMerits -> {
voteListItemList.add(new VoteListItem(evaluatedProposal.getProposal(), decryptedBallotsWithMerits,
daoStateService, bsqFormatter));
});
});
voteListItemList.sort(Comparator.comparing(VoteListItem::getBlindVoteTxId));
GUIUtil.setFitToRowsForTableView(votesTableView, 25, 28, 2, 4);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// TableColumns: CycleListItem // TableColumns: CycleListItem
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -879,117 +810,6 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
votesTableView.getColumns().add(column); votesTableView.getColumns().add(column);
} }
///////////////////////////////////////////////////////////////////////////////////////////
// TableColumns: VoteListItem
///////////////////////////////////////////////////////////////////////////////////////////
private void createColumns(TableView<VoteListItem> votesTableView) {
TableColumn<VoteListItem, VoteListItem> column;
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.vote"));
column.setSortable(false);
column.setMinWidth(50);
column.setMaxWidth(column.getMinWidth());
column.getStyleClass().add("first-column");
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(
TableColumn<VoteListItem, VoteListItem> column) {
return new TableCell<>() {
private Label icon;
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
Tuple2<AwesomeIcon, String> iconStyleTuple = item.getIconStyleTuple();
icon = new Label();
AwesomeDude.setIcon(icon, iconStyleTuple.first);
icon.getStyleClass().add(iconStyleTuple.second);
setGraphic(icon);
} else {
setGraphic(null);
}
}
};
}
});
votesTableView.getColumns().add(column);
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.stakeAndMerit"));
column.setSortable(false);
column.setMinWidth(100);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(
TableColumn<VoteListItem, VoteListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setText(item.getMeritAndStake());
else
setText("");
}
};
}
});
votesTableView.getColumns().add(column);
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.merit"));
column.setSortable(false);
column.setMinWidth(100);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(
TableColumn<VoteListItem, VoteListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setText(item.getMerit());
else
setText("");
}
};
}
});
votesTableView.getColumns().add(column);
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.stake"));
column.setSortable(false);
column.setMinWidth(100);
column.getStyleClass().add("last-column");
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(
TableColumn<VoteListItem, VoteListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setText(item.getStake());
else
setText("");
}
};
}
});
votesTableView.getColumns().add(column);
}
private JsonElement getVotingHistoryJson() { private JsonElement getVotingHistoryJson() {
JsonArray cyclesArray = new JsonArray(); JsonArray cyclesArray = new JsonArray();

View file

@ -17,6 +17,7 @@
package bisq.desktop.main.dao.wallet; package bisq.desktop.main.dao.wallet;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.util.FormBuilder; import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.Layout; import bisq.desktop.util.Layout;
@ -176,10 +177,12 @@ public class BsqBalanceUtil implements BsqBalanceListener, DaoStateListener {
return gridRow; return gridRow;
} }
public int addBondBalanceGroup(GridPane gridPane, int gridRow) { public int addBondBalanceGroup(GridPane gridPane, int gridRow, String groupStyle) {
addTitledGroupBg(gridPane, ++gridRow, 2, TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 2,
Res.get("dao.bond.dashboard.bondsHeadline"), Layout.GROUP_DISTANCE); Res.get("dao.bond.dashboard.bondsHeadline"), Layout.GROUP_DISTANCE);
if (groupStyle != null) titledGroupBg.getStyleClass().add(groupStyle);
lockupAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(gridPane, gridRow, lockupAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(gridPane, gridRow,
Res.get("dao.bond.dashboard.lockupAmount"), Res.get("dao.bond.dashboard.lockupAmount"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;

View file

@ -20,6 +20,7 @@ package bisq.desktop.main.dao.wallet.dashboard;
import bisq.desktop.common.view.ActivatableView; import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView; import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.dao.wallet.BsqBalanceUtil; import bisq.desktop.main.dao.wallet.BsqBalanceUtil;
import bisq.desktop.util.FormBuilder; import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.Layout; import bisq.desktop.util.Layout;
@ -96,24 +97,24 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
int startRow = gridRow; int startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 5, Res.get("dao.wallet.dashboard.distribution"), Layout.GROUP_DISTANCE); addTitledGroupBg(root, ++gridRow, 5, Res.get("dao.wallet.dashboard.distribution"), Layout.GROUP_DISTANCE);
genesisIssueAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisIssueAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; genesisIssueAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisIssueAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
compRequestIssueAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compRequestIssueAmount")).second; compRequestIssueAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compRequestIssueAmount")).second;
reimbursementAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.reimbursementAmount")).second; reimbursementAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.reimbursementAmount")).second;
burntAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second; burntAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second;
availableAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second; availableAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second;
gridRow = startRow; gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 5, Res.get("dao.wallet.dashboard.locked"), Layout.GROUP_DISTANCE); addTitledGroupBg(root, ++gridRow, columnIndex, 5, Res.get("dao.wallet.dashboard.locked"), Layout.GROUP_DISTANCE);
totalLockedUpAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalLockedUpAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; totalLockedUpAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalLockedUpAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
totalUnlockingAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockingAmount")).second; totalUnlockingAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockingAmount")).second;
totalUnlockedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second; totalUnlockedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second;
totalConfiscatedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalConfiscatedAmount")).second; totalConfiscatedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalConfiscatedAmount")).second;
gridRow++; gridRow++;
startRow = gridRow; startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.wallet.dashboard.market"), Layout.GROUP_DISTANCE); addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.wallet.dashboard.market"), Layout.GROUP_DISTANCE);
priceTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.price"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; priceTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.price"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
marketCapTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second; marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second;
gridRow = startRow; gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 2, Res.get("dao.wallet.dashboard.genesis"), Layout.GROUP_DISTANCE); addTitledGroupBg(root, ++gridRow, columnIndex, 2, Res.get("dao.wallet.dashboard.genesis"), Layout.GROUP_DISTANCE);
@ -130,19 +131,21 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", genesisTxId))); hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", genesisTxId)));
startRow = gridRow; startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.dashboard.txDetails"), Layout.GROUP_DISTANCE); TitledGroupBg titledGroupBgTxDetails = addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.dashboard.txDetails"), Layout.GROUP_DISTANCE);
allTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.allTx"), titledGroupBgTxDetails.getStyleClass().add("last");
allTxTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.allTx"),
genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
utxoTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second; utxoTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second;
compensationIssuanceTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, compensationIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.wallet.dashboard.compensationIssuanceTx")).second; Res.get("dao.wallet.dashboard.compensationIssuanceTx")).second;
gridRow = startRow; gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 3, "", Layout.GROUP_DISTANCE); TitledGroupBg titledGroupBgReImbursement = addTitledGroupBg(root, ++gridRow, columnIndex, 3, "", Layout.GROUP_DISTANCE);
reimbursementIssuanceTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, columnIndex, titledGroupBgReImbursement.getStyleClass().add("last");
reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex,
Res.get("dao.wallet.dashboard.reimbursementIssuanceTx"), Res.get("dao.wallet.dashboard.reimbursementIssuanceTx"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
burntTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, burntTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex,
Res.get("dao.wallet.dashboard.burntTx")).second; Res.get("dao.wallet.dashboard.burntTx")).second;
++gridRow; ++gridRow;

View file

@ -67,6 +67,7 @@ public class BsqReceiveView extends ActivatableView<GridPane, Void> {
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 1, TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 1,
Res.get("dao.wallet.receive.fundYourWallet"), Layout.GROUP_DISTANCE); Res.get("dao.wallet.receive.fundYourWallet"), Layout.GROUP_DISTANCE);
titledGroupBg.getStyleClass().add("last");
GridPane.setColumnSpan(titledGroupBg, 3); GridPane.setColumnSpan(titledGroupBg, 3);
Tuple3<Label, BsqAddressTextField, VBox> tuple = addLabelBsqAddressTextField(root, gridRow, Tuple3<Label, BsqAddressTextField, VBox> tuple = addLabelBsqAddressTextField(root, gridRow,
Res.get("dao.wallet.receive.bsqAddress"), Res.get("dao.wallet.receive.bsqAddress"),

View file

@ -125,6 +125,7 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
int extraRows = screenSize <= INITIAL_WINDOW_HEIGHT ? 0 : (int) ((screenSize - INITIAL_WINDOW_HEIGHT) / pixelsPerOfferTableRow); int extraRows = screenSize <= INITIAL_WINDOW_HEIGHT ? 0 : (int) ((screenSize - INITIAL_WINDOW_HEIGHT) / pixelsPerOfferTableRow);
return extraRows == 0 ? initialOfferTableViewHeight : Math.ceil(initialOfferTableViewHeight + ((extraRows + 1) * pixelsPerOfferTableRow)); return extraRows == 0 ? initialOfferTableViewHeight : Math.ceil(initialOfferTableViewHeight + ((extraRows + 1) * pixelsPerOfferTableRow));
}; };
private ChangeListener<Number> bisqWindowVerticalSizeListener;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle // Constructor, lifecycle
@ -141,12 +142,7 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
@Override @Override
public void initialize() { public void initialize() {
changeListener = c -> updateChartData(); createListener();
currencyListItemsListener = c -> {
if (model.getSelectedCurrencyListItem().isPresent())
currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get());
};
final Tuple3<VBox, Label, ComboBox<CurrencyListItem>> currencyComboBoxTuple = addTopLabelComboBox(Res.get("shared.currency"), final Tuple3<VBox, Label, ComboBox<CurrencyListItem>> currencyComboBoxTuple = addTopLabelComboBox(Res.get("shared.currency"),
Res.get("list.currency.select"), 0); Res.get("list.currency.select"), 0);
@ -268,6 +264,23 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
buyOfferTableView.setItems(model.getTopBuyOfferList()); buyOfferTableView.setItems(model.getTopBuyOfferList());
sellOfferTableView.setItems(model.getTopSellOfferList()); sellOfferTableView.setItems(model.getTopSellOfferList());
buyOfferTableView.getSelectionModel().selectedItemProperty().addListener(buyTableRowSelectionListener);
sellOfferTableView.getSelectionModel().selectedItemProperty().addListener(sellTableRowSelectionListener);
root.getScene().heightProperty().addListener(bisqWindowVerticalSizeListener);
updateChartData();
}
private void createListener() {
changeListener = c -> updateChartData();
currencyListItemsListener = c -> {
if (model.getSelectedCurrencyListItem().isPresent())
currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get());
};
buyTableRowSelectionListener = (observable, oldValue, newValue) -> { buyTableRowSelectionListener = (observable, oldValue, newValue) -> {
model.preferences.setSellScreenCurrencyCode(model.getCurrencyCode()); model.preferences.setSellScreenCurrencyCode(model.getCurrencyCode());
navigation.navigateTo(MainView.class, SellOfferView.class); navigation.navigateTo(MainView.class, SellOfferView.class);
@ -276,19 +289,14 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
model.preferences.setBuyScreenCurrencyCode(model.getCurrencyCode()); model.preferences.setBuyScreenCurrencyCode(model.getCurrencyCode());
navigation.navigateTo(MainView.class, BuyOfferView.class); navigation.navigateTo(MainView.class, BuyOfferView.class);
}; };
buyOfferTableView.getSelectionModel().selectedItemProperty().addListener(buyTableRowSelectionListener);
sellOfferTableView.getSelectionModel().selectedItemProperty().addListener(sellTableRowSelectionListener);
ChangeListener<Number> bisqWindowVerticalSizeListener = (observable, oldValue, newValue) -> { bisqWindowVerticalSizeListener = (observable, oldValue, newValue) -> {
double newTableViewHeight = offerTableViewHeight.apply(newValue.doubleValue()); double newTableViewHeight = offerTableViewHeight.apply(newValue.doubleValue());
if (buyOfferTableView.getHeight() != newTableViewHeight) { if (buyOfferTableView.getHeight() != newTableViewHeight) {
buyOfferTableView.setMinHeight(newTableViewHeight); buyOfferTableView.setMinHeight(newTableViewHeight);
sellOfferTableView.setMinHeight(newTableViewHeight); sellOfferTableView.setMinHeight(newTableViewHeight);
} }
}; };
root.getScene().heightProperty().addListener(bisqWindowVerticalSizeListener);
updateChartData();
} }
@Override @Override

View file

@ -60,6 +60,7 @@ import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority; import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate; import javafx.scene.transform.Rotate;
@ -499,7 +500,7 @@ public abstract class Overlay<T extends Overlay> {
if (owner != null) { if (owner != null) {
Scene rootScene = owner.getScene(); Scene rootScene = owner.getScene();
if (rootScene != null) { if (rootScene != null) {
Scene scene = new Scene(gridPane); Scene scene = new Scene(getRootContainer());
scene.getStylesheets().setAll(rootScene.getStylesheets()); scene.getStylesheets().setAll(rootScene.getStylesheets());
scene.setFill(Color.TRANSPARENT); scene.setFill(Color.TRANSPARENT);
@ -544,6 +545,11 @@ public abstract class Overlay<T extends Overlay> {
} }
} }
protected Region getRootContainer() {
return gridPane;
}
protected void setupKeyHandler(Scene scene) { protected void setupKeyHandler(Scene scene) {
if (!hideCloseButton) { if (!hideCloseButton) {
scene.setOnKeyPressed(e -> { scene.setOnKeyPressed(e -> {
@ -556,66 +562,68 @@ public abstract class Overlay<T extends Overlay> {
} }
protected void animateDisplay() { protected void animateDisplay() {
gridPane.setOpacity(0); Region rootContainer = this.getRootContainer();
rootContainer.setOpacity(0);
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1); Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
double duration = getDuration(400); double duration = getDuration(400);
Timeline timeline = new Timeline(); Timeline timeline = new Timeline();
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames(); ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
if (type.animationType == AnimationType.SlideDownFromCenterTop) { if (type.animationType == AnimationType.SlideDownFromCenterTop) {
double startY = -gridPane.getHeight(); double startY = -rootContainer.getHeight();
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator), new KeyValue(rootContainer.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.translateYProperty(), startY, interpolator) new KeyValue(rootContainer.translateYProperty(), startY, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator), new KeyValue(rootContainer.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.translateYProperty(), -50, interpolator) new KeyValue(rootContainer.translateYProperty(), -50, interpolator)
)); ));
} else if (type.animationType == AnimationType.ScaleFromCenter) { } else if (type.animationType == AnimationType.ScaleFromCenter) {
double startScale = 0.25; double startScale = 0.25;
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator), new KeyValue(rootContainer.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), startScale, interpolator), new KeyValue(rootContainer.scaleXProperty(), startScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), startScale, interpolator) new KeyValue(rootContainer.scaleYProperty(), startScale, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator), new KeyValue(rootContainer.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator), new KeyValue(rootContainer.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator) new KeyValue(rootContainer.scaleYProperty(), 1, interpolator)
)); ));
} else if (type.animationType == AnimationType.ScaleYFromCenter) { } else if (type.animationType == AnimationType.ScaleYFromCenter) {
double startYScale = 0.25; double startYScale = 0.25;
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator), new KeyValue(rootContainer.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleYProperty(), startYScale, interpolator) new KeyValue(rootContainer.scaleYProperty(), startYScale, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator), new KeyValue(rootContainer.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator) new KeyValue(rootContainer.scaleYProperty(), 1, interpolator)
)); ));
} else if (type.animationType == AnimationType.ScaleDownToCenter) { } else if (type.animationType == AnimationType.ScaleDownToCenter) {
double startScale = 1.1; double startScale = 1.1;
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator), new KeyValue(rootContainer.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), startScale, interpolator), new KeyValue(rootContainer.scaleXProperty(), startScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), startScale, interpolator) new KeyValue(rootContainer.scaleYProperty(), startScale, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator), new KeyValue(rootContainer.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator), new KeyValue(rootContainer.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator) new KeyValue(rootContainer.scaleYProperty(), 1, interpolator)
)); ));
} else if (type.animationType == AnimationType.FadeInAtCenter) { } else if (type.animationType == AnimationType.FadeInAtCenter) {
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator) new KeyValue(rootContainer.opacityProperty(), 0, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator) new KeyValue(rootContainer.opacityProperty(), 1, interpolator)
)); ));
} }
@ -628,15 +636,16 @@ public abstract class Overlay<T extends Overlay> {
Timeline timeline = new Timeline(); Timeline timeline = new Timeline();
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames(); ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
Region rootContainer = getRootContainer();
if (type.animationType == AnimationType.SlideDownFromCenterTop) { if (type.animationType == AnimationType.SlideDownFromCenterTop) {
double endY = -gridPane.getHeight(); double endY = -rootContainer.getHeight();
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator), new KeyValue(rootContainer.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.translateYProperty(), -10, interpolator) new KeyValue(rootContainer.translateYProperty(), -10, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator), new KeyValue(rootContainer.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.translateYProperty(), endY, interpolator) new KeyValue(rootContainer.translateYProperty(), endY, interpolator)
)); ));
timeline.setOnFinished(e -> onFinishedHandler.run()); timeline.setOnFinished(e -> onFinishedHandler.run());
@ -644,44 +653,44 @@ public abstract class Overlay<T extends Overlay> {
} else if (type.animationType == AnimationType.ScaleFromCenter) { } else if (type.animationType == AnimationType.ScaleFromCenter) {
double endScale = 0.25; double endScale = 0.25;
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator), new KeyValue(rootContainer.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator), new KeyValue(rootContainer.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator) new KeyValue(rootContainer.scaleYProperty(), 1, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator), new KeyValue(rootContainer.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), endScale, interpolator), new KeyValue(rootContainer.scaleXProperty(), endScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), endScale, interpolator) new KeyValue(rootContainer.scaleYProperty(), endScale, interpolator)
)); ));
} else if (type.animationType == AnimationType.ScaleYFromCenter) { } else if (type.animationType == AnimationType.ScaleYFromCenter) {
gridPane.setRotationAxis(Rotate.X_AXIS); rootContainer.setRotationAxis(Rotate.X_AXIS);
gridPane.getScene().setCamera(new PerspectiveCamera()); rootContainer.getScene().setCamera(new PerspectiveCamera());
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.rotateProperty(), 0, interpolator), new KeyValue(rootContainer.rotateProperty(), 0, interpolator),
new KeyValue(gridPane.opacityProperty(), 1, interpolator) new KeyValue(rootContainer.opacityProperty(), 1, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.rotateProperty(), -90, interpolator), new KeyValue(rootContainer.rotateProperty(), -90, interpolator),
new KeyValue(gridPane.opacityProperty(), 0, interpolator) new KeyValue(rootContainer.opacityProperty(), 0, interpolator)
)); ));
} else if (type.animationType == AnimationType.ScaleDownToCenter) { } else if (type.animationType == AnimationType.ScaleDownToCenter) {
double endScale = 0.1; double endScale = 0.1;
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator), new KeyValue(rootContainer.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator), new KeyValue(rootContainer.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator) new KeyValue(rootContainer.scaleYProperty(), 1, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator), new KeyValue(rootContainer.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), endScale, interpolator), new KeyValue(rootContainer.scaleXProperty(), endScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), endScale, interpolator) new KeyValue(rootContainer.scaleYProperty(), endScale, interpolator)
)); ));
} else if (type.animationType == AnimationType.FadeInAtCenter) { } else if (type.animationType == AnimationType.FadeInAtCenter) {
keyFrames.add(new KeyFrame(Duration.millis(0), keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator) new KeyValue(rootContainer.opacityProperty(), 1, interpolator)
)); ));
keyFrames.add(new KeyFrame(Duration.millis(duration), keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator) new KeyValue(rootContainer.opacityProperty(), 0, interpolator)
)); ));
} }
@ -718,10 +727,11 @@ public abstract class Overlay<T extends Overlay> {
protected void applyStyles() { protected void applyStyles() {
Region rootContainer = getRootContainer();
if (type.animationType == AnimationType.SlideDownFromCenterTop) { if (type.animationType == AnimationType.SlideDownFromCenterTop) {
gridPane.getStyleClass().add("popup-bg-top"); rootContainer.getStyleClass().add("popup-bg-top");
} else { } else {
gridPane.getStyleClass().add("popup-bg"); rootContainer.getStyleClass().add("popup-bg");
} }

View file

@ -0,0 +1,20 @@
package bisq.desktop.main.overlays;
import com.jfoenix.controls.JFXTabPane;
import javafx.scene.layout.Region;
public abstract class TabbedOverlay<T extends TabbedOverlay> extends Overlay {
protected JFXTabPane tabPane;
protected void createTabPane() {
this.tabPane = new JFXTabPane();
tabPane.setMinWidth(width);
}
@Override
protected Region getRootContainer() {
return tabPane;
}
}

View file

@ -0,0 +1,326 @@
package bisq.desktop.main.overlays.windows;
import bisq.desktop.Navigation;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.components.TableGroupHeadline;
import bisq.desktop.main.dao.governance.ProposalDisplay;
import bisq.desktop.main.dao.governance.result.VoteListItem;
import bisq.desktop.main.overlays.TabbedOverlay;
import bisq.desktop.util.Layout;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.proposal.param.ChangeParamValidator;
import bisq.core.dao.state.model.governance.Ballot;
import bisq.core.dao.state.model.governance.EvaluatedProposal;
import bisq.core.dao.state.model.governance.Proposal;
import bisq.core.locale.Res;
import bisq.core.user.Preferences;
import bisq.core.util.BsqFormatter;
import bisq.common.util.Tuple2;
import javax.inject.Inject;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import static bisq.desktop.util.FormBuilder.addButtonAfterGroup;
@Slf4j
public class ProposalResultsWindow extends TabbedOverlay<ProposalResultsWindow> {
private final BsqFormatter bsqFormatter;
private final DaoFacade daoFacade;
private final ChangeParamValidator changeParamValidator;
private final Navigation navigation;
private final Preferences preferences;
private Optional<Runnable> acceptHandlerOptional;
private Optional<Runnable> rejectHandlerOptional;
private Optional<Runnable> ignoreHandlerOptional;
private Optional<Runnable> removeHandlerOptional;
private EvaluatedProposal evaluatedProposal;
private Ballot ballot;
private boolean isVoteIncludedInResult;
private SortedList<VoteListItem> sortedVotes;
private Tab proposalTab, votesTab;
///////////////////////////////////////////////////////////////////////////////////////////
// Public API
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public ProposalResultsWindow(BsqFormatter bsqFormatter, DaoFacade daoFacade,
ChangeParamValidator changeParamValidator, Navigation navigation,
Preferences preferences) {
this.bsqFormatter = bsqFormatter;
this.daoFacade = daoFacade;
this.changeParamValidator = changeParamValidator;
this.navigation = navigation;
this.preferences = preferences;
}
public void show(EvaluatedProposal evaluatedProposal, Ballot ballot,
boolean isVoteIncludedInResult, SortedList<VoteListItem> sortedVotes) {
this.evaluatedProposal = evaluatedProposal;
this.ballot = ballot;
this.isVoteIncludedInResult = isVoteIncludedInResult;
this.sortedVotes = sortedVotes;
rowIndex = 0;
width = 1000;
createTabPane();
createGridPane();
addContent(evaluatedProposal, ballot);
display();
}
private void createTabs() {
proposalTab = new Tab("PROPOSAL");
votesTab = new Tab("VOTES");
tabPane.getTabs().addAll(proposalTab, votesTab);
tabPane.getSelectionModel().selectedItemProperty().addListener((ov, oldValue, newValue) -> {
if (newValue != oldValue) {
stage.sizeToScene();
layout();
}
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
@Override
protected void onShow() {
super.onShow();
setupCloseKeyHandler(stage.getScene());
}
@Override
protected void createGridPane() {
super.createGridPane();
gridPane.getColumnConstraints().remove(1);
}
private void addContent(EvaluatedProposal evaluatedProposal, Ballot ballot) {
Proposal proposal = evaluatedProposal.getProposal();
ProposalDisplay proposalDisplay = new ProposalDisplay(gridPane, bsqFormatter, daoFacade, null,
navigation, preferences);
proposalDisplay.createAllFields("", rowIndex, -Layout.FIRST_ROW_DISTANCE, proposal.getType(),
false, "last");
proposalDisplay.setEditable(false);
proposalDisplay.applyProposalPayload(proposal);
proposalDisplay.applyEvaluatedProposal(evaluatedProposal);
proposalDisplay.setIsVoteIncludedInResult(isVoteIncludedInResult);
Tuple2<Long, Long> meritAndStakeTuple = daoFacade.getMeritAndStakeForProposal(proposal.getTxId());
long merit = meritAndStakeTuple.first;
long stake = meritAndStakeTuple.second;
proposalDisplay.applyBallotAndVoteWeight(ballot, merit, stake);
Region spacer = new Region();
GridPane.setVgrow(spacer, Priority.ALWAYS);
GridPane.setRowIndex(spacer, proposalDisplay.incrementAndGetGridRow());
gridPane.getChildren().add(spacer);
addCloseButton(gridPane, proposalDisplay.incrementAndGetGridRow());
createTabs();
gridPane.setPadding(new Insets(0, 15, 15, 15));
proposalTab.setContent(gridPane);
votesTab.setContent(createVotesTable());
}
private Button addCloseButton(GridPane gridPane, int rowIndex) {
Button closeButton = addButtonAfterGroup(gridPane, rowIndex, Res.get("shared.close"));
closeButton.setOnAction(event -> doClose());
return closeButton;
}
private GridPane createVotesTable() {
GridPane votesGridPane = new GridPane();
votesGridPane.setHgap(5);
votesGridPane.setVgap(5);
votesGridPane.setPadding(new Insets(15));
ColumnConstraints columnConstraints1 = new ColumnConstraints();
columnConstraints1.setHalignment(HPos.RIGHT);
columnConstraints1.setHgrow(Priority.ALWAYS);
votesGridPane.getColumnConstraints().addAll(columnConstraints1);
int gridRow = 0;
TableGroupHeadline votesTableHeader = new TableGroupHeadline(Res.get("dao.results.proposals.voting.detail.header"));
GridPane.setRowIndex(votesTableHeader, gridRow);
GridPane.setMargin(votesTableHeader, new Insets(8, 0, 0, 0));
GridPane.setColumnSpan(votesTableHeader, 2);
votesGridPane.getChildren().add(votesTableHeader);
TableView votesTableView = new TableView<>();
votesTableView.setPlaceholder(new AutoTooltipLabel(Res.get("table.placeholder.noData")));
votesTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
createColumns(votesTableView);
GridPane.setRowIndex(votesTableView, gridRow);
GridPane.setMargin(votesTableView, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 0));
GridPane.setColumnSpan(votesTableView, 2);
GridPane.setVgrow(votesTableView, Priority.ALWAYS);
votesGridPane.getChildren().add(votesTableView);
votesTableView.setItems(sortedVotes);
addCloseButton(votesGridPane, ++gridRow);
return votesGridPane;
}
private void createColumns(TableView<VoteListItem> votesTableView) {
TableColumn<VoteListItem, VoteListItem> column;
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.vote"));
column.setSortable(false);
column.setMinWidth(50);
column.setMaxWidth(column.getMinWidth());
column.getStyleClass().add("first-column");
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(
TableColumn<VoteListItem, VoteListItem> column) {
return new TableCell<>() {
private Label icon;
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
Tuple2<AwesomeIcon, String> iconStyleTuple = item.getIconStyleTuple();
icon = new Label();
AwesomeDude.setIcon(icon, iconStyleTuple.first);
icon.getStyleClass().add(iconStyleTuple.second);
setGraphic(icon);
} else {
setGraphic(null);
}
}
};
}
});
votesTableView.getColumns().add(column);
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.stakeAndMerit"));
column.setSortable(false);
column.setMinWidth(100);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(
TableColumn<VoteListItem, VoteListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setText(item.getMeritAndStake());
else
setText("");
}
};
}
});
votesTableView.getColumns().add(column);
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.merit"));
column.setSortable(false);
column.setMinWidth(100);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(
TableColumn<VoteListItem, VoteListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setText(item.getMerit());
else
setText("");
}
};
}
});
votesTableView.getColumns().add(column);
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.stake"));
column.setSortable(false);
column.setMinWidth(100);
column.getStyleClass().add("last-column");
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(
TableColumn<VoteListItem, VoteListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setText(item.getStake());
else
setText("");
}
};
}
});
votesTableView.getColumns().add(column);
}
private void setupCloseKeyHandler(Scene scene) {
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE || e.getCode() == KeyCode.ENTER) {
e.consume();
doClose();
}
});
}
}

View file

@ -0,0 +1,225 @@
package bisq.desktop.main.overlays.windows;
import bisq.desktop.Navigation;
import bisq.desktop.main.dao.governance.ProposalDisplay;
import bisq.desktop.main.overlays.Overlay;
import bisq.desktop.util.Layout;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.myvote.MyVote;
import bisq.core.dao.governance.proposal.param.ChangeParamValidator;
import bisq.core.dao.state.model.governance.Ballot;
import bisq.core.dao.state.model.governance.DaoPhase;
import bisq.core.dao.state.model.governance.EvaluatedProposal;
import bisq.core.dao.state.model.governance.Proposal;
import bisq.core.dao.state.model.governance.Vote;
import bisq.core.locale.Res;
import bisq.core.user.Preferences;
import bisq.core.util.BsqFormatter;
import bisq.common.util.Tuple2;
import bisq.common.util.Tuple3;
import javax.inject.Inject;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import java.util.List;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import static bisq.desktop.util.FormBuilder.add2ButtonsAfterGroup;
import static bisq.desktop.util.FormBuilder.add3ButtonsAfterGroup;
import static bisq.desktop.util.FormBuilder.addButtonAfterGroup;
@Slf4j
public class SelectProposalWindow extends Overlay<SelectProposalWindow> {
private final BsqFormatter bsqFormatter;
private final DaoFacade daoFacade;
private final ChangeParamValidator changeParamValidator;
private final Navigation navigation;
private final Preferences preferences;
private Optional<Runnable> acceptHandlerOptional;
private Optional<Runnable> rejectHandlerOptional;
private Optional<Runnable> ignoreHandlerOptional;
private Optional<Runnable> removeHandlerOptional;
private Proposal proposal;
private EvaluatedProposal evaluatedProposal;
private Ballot ballot;
///////////////////////////////////////////////////////////////////////////////////////////
// Public API
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public SelectProposalWindow(BsqFormatter bsqFormatter, DaoFacade daoFacade,
ChangeParamValidator changeParamValidator, Navigation navigation,
Preferences preferences) {
this.bsqFormatter = bsqFormatter;
this.daoFacade = daoFacade;
this.changeParamValidator = changeParamValidator;
this.navigation = navigation;
this.preferences = preferences;
}
public void show(Proposal proposal, EvaluatedProposal evaluatedProposal, Ballot ballot) {
this.proposal = proposal;
this.evaluatedProposal = evaluatedProposal;
this.ballot = ballot;
rowIndex = 0;
width = 1000;
createGridPane();
headLine(Res.get("dao.proposal.selectedProposal"));
message("");
hideCloseButton();
super.show();
}
public void onAccept(Runnable acceptHandler) {
this.acceptHandlerOptional = Optional.of(acceptHandler);
}
public void onReject(Runnable rejectHandler) {
this.rejectHandlerOptional = Optional.of(rejectHandler);
}
public void onIgnore(Runnable ignoreHandler) {
this.ignoreHandlerOptional = Optional.of(ignoreHandler);
}
public void onRemove(Runnable removeHandler) {
this.removeHandlerOptional = Optional.of(removeHandler);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
@Override
protected void createGridPane() {
super.createGridPane();
gridPane.getColumnConstraints().remove(1);
}
@Override
protected void onShow() {
display();
setupCloseKeyHandler(stage.getScene());
}
@Override
protected void addMessage() {
super.addMessage();
addContent(proposal, evaluatedProposal, ballot);
}
private void addContent(Proposal proposal, EvaluatedProposal evaluatedProposal, Ballot ballot) {
ProposalDisplay proposalDisplay = new ProposalDisplay(gridPane, bsqFormatter, daoFacade, changeParamValidator,
navigation, preferences);
proposalDisplay.createAllFields("", rowIndex, -Layout.FIRST_ROW_DISTANCE, proposal.getType(),
false, "last");
proposalDisplay.setEditable(false);
proposalDisplay.applyProposalPayload(proposal);
proposalDisplay.applyEvaluatedProposal(evaluatedProposal);
Tuple2<Long, Long> meritAndStakeTuple = daoFacade.getMeritAndStakeForProposal(proposal.getTxId());
long merit = meritAndStakeTuple.first;
long stake = meritAndStakeTuple.second;
proposalDisplay.applyBallotAndVoteWeight(ballot, merit, stake);
List<MyVote> myVoteListForCycle = daoFacade.getMyVoteListForCycle();
boolean hasAlreadyVoted = !myVoteListForCycle.isEmpty();
DaoPhase.Phase currentPhase = daoFacade.phaseProperty().get();
if (currentPhase == DaoPhase.Phase.PROPOSAL) {
Tuple2<Button, Button> proposalPhaseButtonsTuple = add2ButtonsAfterGroup(gridPane, proposalDisplay.incrementAndGetGridRow(), Res.get("shared.remove"), Res.get("shared.close"));
Button removeProposalButton = proposalPhaseButtonsTuple.first;
boolean doShowRemoveButton = daoFacade.isMyProposal(proposal);
removeProposalButton.setOnAction(event -> {
removeHandlerOptional.ifPresent(Runnable::run);
doClose();
});
removeProposalButton.setVisible(doShowRemoveButton);
removeProposalButton.setManaged(doShowRemoveButton);
proposalPhaseButtonsTuple.second.setOnAction(event -> doClose());
} else if (currentPhase == DaoPhase.Phase.BLIND_VOTE &&
!hasAlreadyVoted &&
daoFacade.isInPhaseButNotLastBlock(currentPhase)) {
int rowIndexForVoting = proposalDisplay.incrementAndGetGridRow();
Tuple3<Button, Button, Button> tuple = add3ButtonsAfterGroup(gridPane,
rowIndexForVoting,
Res.get("dao.proposal.myVote.accept"),
Res.get("dao.proposal.myVote.reject"),
Res.get("dao.proposal.myVote.removeMyVote"));
Button acceptButton = tuple.first;
acceptButton.setDefaultButton(false);
Button rejectButton = tuple.second;
Button ignoreButton = tuple.third;
// show if already voted
proposalDisplay.applyBallot(ballot);
Optional<Vote> optionalVote = getVote(ballot);
boolean isPresent = optionalVote.isPresent();
boolean isAccepted = isPresent && optionalVote.get().isAccepted();
acceptButton.setDisable((isPresent && isAccepted));
rejectButton.setDisable((isPresent && !isAccepted));
ignoreButton.setDisable(!isPresent);
acceptButton.setOnAction(event -> {
acceptHandlerOptional.ifPresent(Runnable::run);
doClose();
});
rejectButton.setOnAction(event -> {
rejectHandlerOptional.ifPresent(Runnable::run);
doClose();
});
ignoreButton.setOnAction(event -> {
ignoreHandlerOptional.ifPresent(Runnable::run);
doClose();
});
Button closeButton = addButtonAfterGroup(gridPane, ++rowIndexForVoting, Res.get("shared.close"));
closeButton.setOnAction(event -> doClose());
} else {
Button closeButton = addButtonAfterGroup(gridPane, proposalDisplay.incrementAndGetGridRow(), Res.get("shared.close"));
closeButton.setOnAction(event -> doClose());
}
}
private void setupCloseKeyHandler(Scene scene) {
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE || e.getCode() == KeyCode.ENTER) {
e.consume();
doClose();
}
});
}
private Optional<Vote> getVote(@Nullable Ballot ballot) {
if (ballot == null)
return Optional.empty();
else
return ballot.getVoteAsOptional();
}
}

View file

@ -1743,11 +1743,21 @@ public class FormBuilder {
} }
public static <T> TableView<T> addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText) { public static <T> TableView<T> addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText) {
return addTableViewWithHeader(gridPane, rowIndex, headerText, 0); return addTableViewWithHeader(gridPane, rowIndex, headerText, 0, null);
}
public static <T> TableView<T> addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText, String groupStyle) {
return addTableViewWithHeader(gridPane, rowIndex, headerText, 0, groupStyle);
} }
public static <T> TableView<T> addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText, int top) { public static <T> TableView<T> addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText, int top) {
addTitledGroupBg(gridPane, rowIndex, 1, headerText, top); return addTableViewWithHeader(gridPane, rowIndex, headerText, top, null);
}
public static <T> TableView<T> addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText, int top, String groupStyle) {
TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, rowIndex, 1, headerText, top);
if (groupStyle != null) titledGroupBg.getStyleClass().add(groupStyle);
TableView<T> tableView = new TableView<>(); TableView<T> tableView = new TableView<>();
GridPane.setRowIndex(tableView, rowIndex); GridPane.setRowIndex(tableView, rowIndex);

View file

@ -28,9 +28,11 @@ public class Layout {
public static final double FLOATING_LABEL_DISTANCE = 20d; public static final double FLOATING_LABEL_DISTANCE = 20d;
public static final double GROUP_DISTANCE = 40d; public static final double GROUP_DISTANCE = 40d;
public static final double COMPACT_GROUP_DISTANCE = 30d; public static final double COMPACT_GROUP_DISTANCE = 30d;
public static final double GROUP_DISTANCE_WITHOUT_SEPARATOR = 20d;
public static final double FIRST_ROW_AND_GROUP_DISTANCE = GROUP_DISTANCE + FIRST_ROW_DISTANCE; public static final double FIRST_ROW_AND_GROUP_DISTANCE = GROUP_DISTANCE + FIRST_ROW_DISTANCE;
public static final double COMPACT_FIRST_ROW_AND_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + FIRST_ROW_DISTANCE; public static final double COMPACT_FIRST_ROW_AND_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + FIRST_ROW_DISTANCE;
public static final double COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + COMPACT_FIRST_ROW_DISTANCE; public static final double COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + COMPACT_FIRST_ROW_DISTANCE;
public static final double COMPACT_FIRST_ROW_AND_GROUP_DISTANCE_WITHOUT_SEPARATOR = GROUP_DISTANCE_WITHOUT_SEPARATOR + COMPACT_FIRST_ROW_DISTANCE;
public static final double TWICE_FIRST_ROW_AND_GROUP_DISTANCE = GROUP_DISTANCE + TWICE_FIRST_ROW_DISTANCE; public static final double TWICE_FIRST_ROW_AND_GROUP_DISTANCE = GROUP_DISTANCE + TWICE_FIRST_ROW_DISTANCE;
public static final double PADDING_WINDOW = 20d; public static final double PADDING_WINDOW = 20d;
public static double PADDING = 10d; public static double PADDING = 10d;