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.
dao.proposal.active.remove.doRemove=Yes, remove my proposal
dao.proposal.active.remove.failed=Could not remove proposal.
dao.proposal.myVote.title=Voting
dao.proposal.myVote.accept=Accept proposal
dao.proposal.myVote.reject=Reject 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.blindVoteTxId=Blind vote transaction ID
dao.proposal.myVote.revealTxId=Vote reveal transaction ID
dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0}
dao.proposal.votes.header=Vote on all proposals
dao.proposal.votes.header.voted=My vote
dao.proposal.myVote.button=Vote on all proposals
dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0}
dao.proposal.votes.header=Set stake for voting and publish your votes
dao.proposal.myVote.button=Publish votes
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.phase.inactive=Please wait until the next proposal phase
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;
}
.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-checked-color: -bs-rd-green;
-fx-font-size: 0.692em;

View file

@ -93,7 +93,7 @@ public class BondsView extends ActivatableView<GridPane, Void> {
@Override
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);
addColumns();

View file

@ -42,7 +42,7 @@ public class BondingDashboardView extends ActivatableView<GridPane, Void> {
public void initialize() {
gridRow = bsqBalanceUtil.addGroup(root, gridRow);
gridRow = bsqBalanceUtil.addBondBalanceGroup(root, gridRow);
gridRow = bsqBalanceUtil.addBondBalanceGroup(root, gridRow, "last");
}
@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"));
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();
tableView.setItems(sortedList);

View file

@ -93,7 +93,7 @@ public class RolesView extends ActivatableView<GridPane, Void> {
@Override
public void initialize() {
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();
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"));
tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.burnBsq.allAssets"), 20);
tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.burnBsq.allAssets"), 20, "last");
createColumns();
tableView.setItems(sortedList);

View file

@ -143,7 +143,7 @@ public class ProofOfBurnView extends ActivatableView<GridPane, Void> implements
createColumnsForMyItems();
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();
allTxsTableView.setItems(allItemsSortedList);

View file

@ -140,9 +140,10 @@ public class ProposalDisplay {
private final ChangeListener<Object> inputListener;
private ChangeListener<Param> paramChangeListener;
private ChangeListener<BondedRoleType> requiredBondForRoleListener;
private TitledGroupBg titledGroupBg;
private TitledGroupBg myVoteTitledGroup;
private int titledGroupBgRowSpan;
private VBox linkWithIconContainer, comboBoxValueContainer, myVoteBox, voteResultBox;
private int votingBoxRowSpan;
public ProposalDisplay(GridPane gridPane,
BsqFormatter bsqFormatter,
@ -176,6 +177,11 @@ public class ProposalDisplay {
public void createAllFields(String title, int gridRowStartIndex, double top, ProposalType proposalType,
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();
this.gridRowStartIndex = gridRowStartIndex;
this.gridRow = gridRowStartIndex;
@ -200,12 +206,26 @@ public class ProposalDisplay {
break;
}
titledGroupBg = addTitledGroupBg(gridPane, gridRow, titledGroupBgRowSpan, title, top);
double proposalTypeTop = top == Layout.GROUP_DISTANCE ? Layout.FIRST_ROW_AND_GROUP_DISTANCE : Layout.FIRST_ROW_DISTANCE;
TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, gridRow, titledGroupBgRowSpan, title, top);
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,
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)
nameTextField.setValidator(new InputValidator());
inputControls.add(nameTextField);
@ -317,7 +337,7 @@ public class ProposalDisplay {
}
});
comboBoxes.add(bondedRoleTypeComboBox);
requiredBondForRoleTextField = addTopLabelTextField(gridPane, ++gridRow,
requiredBondForRoleTextField = addTopLabelReadOnlyTextField(gridPane, ++gridRow,
Res.get("dao.proposal.display.requiredBondForRole.label")).second;
requiredBondForRoleListener = (observable, oldValue, newValue) -> {
@ -398,15 +418,18 @@ public class ProposalDisplay {
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.setVisible(false);
myVoteBox.setManaged(false);
setMyVoteBoxVisibility(false);
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.setVisible(false);
@ -427,12 +450,10 @@ public class ProposalDisplay {
}
myVoteTextField.setText(myVote);
myVoteBox.setVisible(isNotNull);
myVoteBox.setManaged(isNotNull);
setMyVoteBoxVisibility(isNotNull);
}
public void applyEvaluatedProposal(@Nullable EvaluatedProposal evaluatedProposal) {
GridPane.setRowSpan(titledGroupBg, titledGroupBgRowSpan + 1);
boolean isEvaluatedProposalNotNull = evaluatedProposal != null;
if (isEvaluatedProposalNotNull) {
@ -468,11 +489,12 @@ public class ProposalDisplay {
String myVoteSummary = Res.get("dao.proposal.myVote.summary", myVote,
weight, meritString, stakeString);
myVoteTextField.setText(myVoteSummary);
GridPane.setRowSpan(myVoteTitledGroup, votingBoxRowSpan - 1);
}
boolean show = ballotIsNotNull && hasVoted;
myVoteBox.setVisible(show);
myVoteBox.setManaged(show);
setMyVoteBoxVisibility(show);
}
public void setIsVoteIncludedInResult(boolean isVoteIncludedInResult) {
@ -658,4 +680,11 @@ public class ProposalDisplay {
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.FxmlView;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.dao.governance.PhasesView;
import bisq.desktop.util.Layout;
@ -69,7 +70,8 @@ public class GovernanceDashboardView extends ActivatableView<GridPane, Void> imp
public void initialize() {
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"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).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);
proposalTitledGroup = addTitledGroupBg(root, ++gridRow, 2, proposalGroupTitle.get(), Layout.GROUP_DISTANCE);
proposalTitledGroup.getStyleClass().add("last");
final Tuple3<Label, TextField, VBox> nextProposalPhaseTuple = addTopLabelReadOnlyTextField(root, gridRow,
Res.get("dao.cycle.proposal.next"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE);
@ -444,7 +445,7 @@ public class MakeProposalView extends ActivatableView<GridPane, Void> implements
if (selectedProposalType != 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);
final Tuple4<Button, BusyAnimation, Label, HBox> makeProposalTuple = addButtonBusyAnimationLabelAfterGroup(root,

View file

@ -17,7 +17,6 @@
package bisq.desktop.main.dao.governance.proposals;
import bisq.desktop.Navigation;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipLabel;
@ -29,8 +28,8 @@ import bisq.desktop.components.TableGroupHeadline;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.components.TxIdTextField;
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.windows.SelectProposalWindow;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout;
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.governance.blindvote.BlindVoteConsensus;
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.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block;
@ -71,7 +69,6 @@ import com.jfoenix.controls.JFXButton;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
@ -101,11 +98,13 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import static bisq.desktop.util.FormBuilder.*;
import static bisq.desktop.util.Layout.INITIAL_WINDOW_HEIGHT;
@FxmlView
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 PhasesView phasesView;
private final DaoStateService daoStateService;
private final ChangeParamValidator changeParamValidator;
private final Preferences preferences;
private final BsqFormatter bsqFormatter;
private final BSFormatter btcFormatter;
private final Navigation navigation;
private final SelectProposalWindow selectProposalWindow;
private final ObservableList<ProposalsListItem> listItems = FXCollections.observableArrayList();
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 TableView<ProposalsListItem> tableView;
private TitledGroupBg voteTitledGroupBg;
private Label voteButtonInfoLabel;
private TxIdTextField revealTxIdTextField, blindVoteTxIdTextField;
private TextField meritTextField;
private VBox blindVoteTxIdContainer, revealTxIdContainer;
private Button removeProposalButton, acceptButton, rejectButton, ignoreButton, voteButton;
private Button voteButton;
private InputTextField stakeInputTextField;
private ScrollPane proposalDisplayView;
private GridPane proposalDisplayGridPane;
private BusyAnimation voteButtonBusyAnimation;
private ProposalDisplay proposalDisplay;
private int gridRow = 0;
private boolean proposalDisplayInitialized;
private ProposalsListItem selectedItem;
private DaoPhase.Phase currentPhase;
private ListChangeListener<Proposal> proposalListChangeListener;
@ -147,6 +140,16 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private Subscription selectedProposalSubscription, phaseSubscription;
private boolean areVoteButtonsVisible;
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,
PhasesView phasesView,
DaoStateService daoStateService,
ChangeParamValidator changeParamValidator,
Preferences preferences,
BsqFormatter bsqFormatter,
BSFormatter btcFormatter,
Navigation navigation) {
SelectProposalWindow selectProposalWindow) {
this.daoFacade = daoFacade;
this.bsqWalletService = bsqWalletService;
this.phasesView = phasesView;
this.daoStateService = daoStateService;
this.changeParamValidator = changeParamValidator;
this.preferences = preferences;
this.bsqFormatter = bsqFormatter;
this.btcFormatter = btcFormatter;
this.navigation = navigation;
this.selectProposalWindow = selectProposalWindow;
}
@Override
@ -180,15 +181,21 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
gridRow = phasesView.addGroup(root, gridRow);
proposalDisplayGridPane = new GridPane();
createProposalsTableView();
createEmptyProposalDisplay();
createVoteView();
ballotListChangeListener = 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();
}
@ -218,8 +225,10 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
bsqWalletService.getUnlockingBondsBalance());
updateListItems();
GUIUtil.setFitToRowsForTableView(tableView, 38, 28, 2, 6);
GUIUtil.setFitToRowsForTableView(tableView, 38, 28, 4, 4);
updateViews();
root.getScene().heightProperty().addListener(bisqWindowVerticalSizeListener);
}
@Override
@ -242,8 +251,6 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
}
if (voteButton != null)
voteButton.setOnAction(null);
if (removeProposalButton != null)
removeProposalButton.setOnAction(null);
listItems.forEach(ProposalsListItem::cleanup);
tableView.getSelectionModel().clearSelection();
@ -316,89 +323,39 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
listItems.clear();
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,
@Nullable EvaluatedProposal evaluatedProposal) {
proposalDisplayView.setVisible(true);
proposalDisplayView.setManaged(true);
private void showVoteOnProposalWindow(Proposal proposal, @Nullable Ballot ballot,
@Nullable EvaluatedProposal evaluatedProposal) {
proposalDisplay.createAllFields(Res.get("dao.proposal.selectedProposal"), 0, 0, proposal.getType(),
false);
proposalDisplay.setEditable(false);
if (!shownVoteOnProposalWindowForTxId.equals(proposal.getTxId())) {
shownVoteOnProposalWindowForTxId = proposal.getTxId();
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);
Tuple2<Long, Long> meritAndStakeTuple = daoFacade.getMeritAndStakeForProposal(proposal.getTxId());
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);
selectProposalWindow.onClose(() -> {
shownVoteOnProposalWindowForTxId = "";
tableView.getSelectionModel().clearSelection();
});
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Handlers
///////////////////////////////////////////////////////////////////////////////////////////
@ -407,15 +364,6 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
if (phase != null && !phase.equals(currentPhase)) {
currentPhase = phase;
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();
@ -427,9 +375,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
new Popup<>().warning(Res.get("dao.proposal.active.remove.confirm"))
.actionButtonText(Res.get("dao.proposal.active.remove.doRemove"))
.onAction(() -> {
if (daoFacade.removeMyProposal(proposal)) {
hideProposalDisplay();
} else {
if (!daoFacade.removeMyProposal(proposal)) {
new Popup<>().warning(Res.get("dao.proposal.active.remove.failed")).show();
}
tableView.getSelectionModel().clearSelection();
@ -448,14 +394,11 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
.findAny()
.orElse(null);
createAllFieldsOnProposalDisplay(selectedItem.getProposal(), selectedItem.getBallot(), evaluatedProposal);
applyMerit();
} else {
hideProposalDisplay();
showVoteOnProposalWindow(selectedItem.getProposal(), selectedItem.getBallot(), evaluatedProposal);
}
onPhaseChanged(daoFacade.phaseProperty().get());
updateViews();
onPhaseChanged(daoFacade.phaseProperty().get());
}
private void applyMerit() {
@ -479,20 +422,31 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private void onAccept() {
daoFacade.setVote(getBallotListItem().getBallot(), new Vote(true));
proposalDisplay.applyBallot(getBallotListItem().getBallot());
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() {
daoFacade.setVote(getBallotListItem().getBallot(), new Vote(false));
proposalDisplay.applyBallot(getBallotListItem().getBallot());
updateStateAfterVote();
tableView.getSelectionModel().clearSelection();
showHowToSetStakeForVotingPopup();
}
private void onIgnore() {
daoFacade.setVote(getBallotListItem().getBallot(), null);
proposalDisplay.applyBallot(getBallotListItem().getBallot());
updateStateAfterVote();
tableView.getSelectionModel().clearSelection();
showHowToSetStakeForVotingPopup();
}
private void onVote() {
@ -545,13 +499,6 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
return selectedItem;
}
private Optional<Vote> getVote(@Nullable Ballot ballot) {
if (ballot == null)
return Optional.empty();
else
return ballot.getVoteAsOptional();
}
private void updateViews() {
boolean isBlindVotePhaseButNotLastBlock = isBlindVotePhaseButNotLastBlock();
boolean hasVotedOnProposal = hasVotedOnProposal();
@ -560,17 +507,8 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
List<MyVote> myVoteListForCycle = daoFacade.getMyVoteListForCycle();
boolean hasAlreadyVoted = !myVoteListForCycle.isEmpty();
if (selectedItem != null && acceptButton != 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);
if (selectedItem != null) {
stakeInputTextField.setMouseTransparent(hasAlreadyVoted || !isBlindVotePhaseButNotLastBlock);
} else {
stakeInputTextField.setMouseTransparent(true);
}
boolean hasProposals = !daoFacade.getActiveOrMyUnconfirmedProposals().isEmpty();
@ -595,7 +533,6 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
revealTxIdContainer.setManaged(false);
if (hasAlreadyVoted) {
voteTitledGroupBg.setText(Res.get("dao.proposal.votes.header.voted"));
if (myVoteListForCycle.size() == 1) {
Optional<MyVote> optionalMyVote = myVoteListForCycle.stream()
.filter(myVote -> daoFacade.isTxInCorrectCycle(myVote.getHeight(), daoFacade.getChainHeight()))
@ -660,7 +597,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
///////////////////////////////////////////////////////////////////////////////////////////
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.setMargin(proposalsHeadline, new Insets(Layout.GROUP_DISTANCE, -10, -10, -10));
root.getChildren().add(proposalsHeadline);
@ -672,25 +609,17 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
createProposalColumns();
GridPane.setRowIndex(tableView, gridRow);
GridPane.setHgrow(tableView, Priority.ALWAYS);
GridPane.setVgrow(tableView, Priority.SOMETIMES);
GridPane.setMargin(tableView, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, -10, 5, -10));
root.getChildren().add(tableView);
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() {
voteTitledGroupBg = addTitledGroupBg(root, ++gridRow, 4,
TitledGroupBg voteTitledGroupBg = addTitledGroupBg(root, ++gridRow, 4,
Res.get("dao.proposal.votes.header"), 20);
voteTitledGroupBg.getStyleClass().add("last");
voteFields.add(voteTitledGroupBg);
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();
if (iconButton != null) {
iconButton.setOnAction(e -> {
onSelectProposal(item);
selectedItem = item;
if (areVoteButtonsVisible) {
if (iconButton.getUserData() == ProposalsListItem.IconButtonTypes.ACCEPT)
onReject();

View file

@ -17,7 +17,6 @@
package bisq.desktop.main.dao.governance.result;
import bisq.desktop.Navigation;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipLabel;
@ -25,9 +24,9 @@ import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.TableGroupHeadline;
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.windows.DaoTestingFeedbackWindow;
import bisq.desktop.main.overlays.windows.ProposalResultsWindow;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil;
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.locale.Res;
import bisq.core.user.DontShowAgainLookup;
import bisq.core.user.Preferences;
import bisq.core.util.BsqFormatter;
import bisq.common.UserThread;
import bisq.common.util.Tuple2;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
@ -79,21 +76,18 @@ import com.google.gson.JsonObject;
import javax.inject.Inject;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;
import javafx.stage.Stage;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
@ -118,6 +112,8 @@ import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static bisq.desktop.util.FormBuilder.addButton;
@FxmlView
public class VoteResultView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private final DaoFacade daoFacade;
@ -128,11 +124,10 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
private final ProposalService proposalService;
private final PeriodService periodService;
private final BsqWalletService bsqWalletService;
private final Preferences preferences;
private final BsqFormatter bsqFormatter;
private final Navigation navigation;
private final MyProposalListService myProposalListService;
private final MyBlindVoteListService myBlindVoteListService;
private ProposalResultsWindow proposalResultsWindow;
private Button exportButton;
private int gridRow = 0;
@ -152,7 +147,6 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
private ChangeListener<CycleListItem> selectedVoteResultListItemListener;
private ResultsOfCycle resultsOfCycle;
private ProposalListItem selectedProposalListItem;
private TableView<VoteListItem> votesTableView;
///////////////////////////////////////////////////////////////////////////////////////////
@ -168,11 +162,10 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
ProposalService proposalService,
PeriodService periodService,
BsqWalletService bsqWalletService,
Preferences preferences,
BsqFormatter bsqFormatter,
Navigation navigation,
MyProposalListService myProposalListService,
MyBlindVoteListService myBlindVoteListService) {
MyBlindVoteListService myBlindVoteListService,
ProposalResultsWindow proposalResultsWindow) {
this.daoFacade = daoFacade;
this.phasesView = phasesView;
this.daoStateService = daoStateService;
@ -181,11 +174,10 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
this.proposalService = proposalService;
this.periodService = periodService;
this.bsqWalletService = bsqWalletService;
this.preferences = preferences;
this.bsqFormatter = bsqFormatter;
this.navigation = navigation;
this.myProposalListService = myProposalListService;
this.myBlindVoteListService = myBlindVoteListService;
this.proposalResultsWindow = proposalResultsWindow;
}
@Override
@ -195,8 +187,9 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
selectedVoteResultListItemListener = (observable, oldValue, newValue) -> onResultsListItemSelected(newValue);
createCyclesTable();
exportButton = FormBuilder.addButton(root, ++gridRow, Res.get("shared.exportJSON"));
GridPane.setMargin(exportButton, new Insets(20, -10, -40, 0));
exportButton = addButton(root, ++gridRow, Res.get("shared.exportJSON"));
exportButton.getStyleClass().add("text-button");
GridPane.setMargin(exportButton, new Insets(10, -10, -50, 0));
GridPane.setColumnSpan(exportButton, 2);
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());
});
if (proposalsTableView != null) {
GUIUtil.setFitToRowsForTableView(proposalsTableView, 25, 28, 2, 4);
GUIUtil.setFitToRowsForTableView(proposalsTableView, 25, 28, 6, 6);
}
if (votesTableView != null) {
GUIUtil.setFitToRowsForTableView(votesTableView, 25, 28, 2, 4);
}
GUIUtil.setFitToRowsForTableView(cyclesTableView, 25, 28, 2, 4);
GUIUtil.setFitToRowsForTableView(cyclesTableView, 25, 28, 6, 6);
}
@Override
@ -336,17 +326,30 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
Optional<Ballot> optionalBallot = daoFacade.getAllValidBallots().stream()
.filter(ballot -> ballot.getTxId().equals(evaluatedProposal.getProposalTxId()))
.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
boolean isVoteIncludedInResult = voteListItemList.stream()
.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
@ -386,7 +389,7 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
maybeShowDaoTestingFeedbackWindow();
GUIUtil.setFitToRowsForTableView(cyclesTableView, 25, 28, 2, 4);
GUIUtil.setFitToRowsForTableView(cyclesTableView, 25, 28, 6, 6);
}
private void maybeShowDaoTestingFeedbackWindow() {
@ -450,7 +453,7 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
createProposalsColumns(proposalsTableView);
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);
root.getChildren().add(proposalsTableView);
@ -477,81 +480,9 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
ballotByProposalTxIdMap.get(evaluatedProposal.getProposalTxId()),
bsqFormatter))
.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
///////////////////////////////////////////////////////////////////////////////////////////
@ -879,117 +810,6 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
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() {
JsonArray cyclesArray = new JsonArray();

View file

@ -17,6 +17,7 @@
package bisq.desktop.main.dao.wallet;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.Layout;
@ -176,10 +177,12 @@ public class BsqBalanceUtil implements BsqBalanceListener, DaoStateListener {
return gridRow;
}
public int addBondBalanceGroup(GridPane gridPane, int gridRow) {
addTitledGroupBg(gridPane, ++gridRow, 2,
public int addBondBalanceGroup(GridPane gridPane, int gridRow, String groupStyle) {
TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 2,
Res.get("dao.bond.dashboard.bondsHeadline"), Layout.GROUP_DISTANCE);
if (groupStyle != null) titledGroupBg.getStyleClass().add(groupStyle);
lockupAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(gridPane, gridRow,
Res.get("dao.bond.dashboard.lockupAmount"),
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.FxmlView;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.dao.wallet.BsqBalanceUtil;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.Layout;
@ -96,24 +97,24 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
int startRow = gridRow;
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;
compRequestIssueAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compRequestIssueAmount")).second;
reimbursementAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.reimbursementAmount")).second;
burntAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second;
availableAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second;
genesisIssueAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisIssueAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
compRequestIssueAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compRequestIssueAmount")).second;
reimbursementAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.reimbursementAmount")).second;
burntAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second;
availableAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second;
gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 5, Res.get("dao.wallet.dashboard.locked"), Layout.GROUP_DISTANCE);
totalLockedUpAmountTextField = FormBuilder.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;
totalUnlockedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second;
totalConfiscatedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalConfiscatedAmount")).second;
totalLockedUpAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalLockedUpAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
totalUnlockingAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockingAmount")).second;
totalUnlockedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second;
totalConfiscatedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalConfiscatedAmount")).second;
gridRow++;
startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.wallet.dashboard.market"), Layout.GROUP_DISTANCE);
priceTextField = FormBuilder.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;
priceTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.price"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second;
gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 2, Res.get("dao.wallet.dashboard.genesis"), Layout.GROUP_DISTANCE);
@ -130,19 +131,21 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", genesisTxId)));
startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.dashboard.txDetails"), Layout.GROUP_DISTANCE);
allTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.allTx"),
TitledGroupBg titledGroupBgTxDetails = addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.dashboard.txDetails"), Layout.GROUP_DISTANCE);
titledGroupBgTxDetails.getStyleClass().add("last");
allTxTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.allTx"),
genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
utxoTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second;
compensationIssuanceTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow,
utxoTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second;
compensationIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.wallet.dashboard.compensationIssuanceTx")).second;
gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 3, "", Layout.GROUP_DISTANCE);
reimbursementIssuanceTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, columnIndex,
TitledGroupBg titledGroupBgReImbursement = addTitledGroupBg(root, ++gridRow, columnIndex, 3, "", Layout.GROUP_DISTANCE);
titledGroupBgReImbursement.getStyleClass().add("last");
reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex,
Res.get("dao.wallet.dashboard.reimbursementIssuanceTx"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
burntTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex,
burntTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex,
Res.get("dao.wallet.dashboard.burntTx")).second;
++gridRow;

View file

@ -67,6 +67,7 @@ public class BsqReceiveView extends ActivatableView<GridPane, Void> {
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 1,
Res.get("dao.wallet.receive.fundYourWallet"), Layout.GROUP_DISTANCE);
titledGroupBg.getStyleClass().add("last");
GridPane.setColumnSpan(titledGroupBg, 3);
Tuple3<Label, BsqAddressTextField, VBox> tuple = addLabelBsqAddressTextField(root, gridRow,
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);
return extraRows == 0 ? initialOfferTableViewHeight : Math.ceil(initialOfferTableViewHeight + ((extraRows + 1) * pixelsPerOfferTableRow));
};
private ChangeListener<Number> bisqWindowVerticalSizeListener;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
@ -141,12 +142,7 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
@Override
public void initialize() {
changeListener = c -> updateChartData();
currencyListItemsListener = c -> {
if (model.getSelectedCurrencyListItem().isPresent())
currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get());
};
createListener();
final Tuple3<VBox, Label, ComboBox<CurrencyListItem>> currencyComboBoxTuple = addTopLabelComboBox(Res.get("shared.currency"),
Res.get("list.currency.select"), 0);
@ -268,6 +264,23 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
buyOfferTableView.setItems(model.getTopBuyOfferList());
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) -> {
model.preferences.setSellScreenCurrencyCode(model.getCurrencyCode());
navigation.navigateTo(MainView.class, SellOfferView.class);
@ -276,19 +289,14 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
model.preferences.setBuyScreenCurrencyCode(model.getCurrencyCode());
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());
if (buyOfferTableView.getHeight() != newTableViewHeight) {
buyOfferTableView.setMinHeight(newTableViewHeight);
sellOfferTableView.setMinHeight(newTableViewHeight);
}
};
root.getScene().heightProperty().addListener(bisqWindowVerticalSizeListener);
updateChartData();
}
@Override

View file

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