From 21109d30748ad3c0b98adf17af7957814fa7bfba Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 15 Aug 2018 18:40:24 +0200 Subject: [PATCH] Add info icon for rounding of fiat values --- .../components/InfoInputTextField.java | 21 +++- .../desktop/components/InfoTextField.java | 99 ++++++++++++++----- .../desktop/main/offer/MutableOfferView.java | 19 +++- .../main/offer/takeoffer/TakeOfferView.java | 25 ++++- .../java/bisq/desktop/util/FormBuilder.java | 19 ++++ 5 files changed, 150 insertions(+), 33 deletions(-) diff --git a/src/main/java/bisq/desktop/components/InfoInputTextField.java b/src/main/java/bisq/desktop/components/InfoInputTextField.java index 326867cb637..1215937dc8a 100644 --- a/src/main/java/bisq/desktop/components/InfoInputTextField.java +++ b/src/main/java/bisq/desktop/components/InfoInputTextField.java @@ -46,6 +46,9 @@ public class InfoInputTextField extends AnchorPane { private final Label infoIcon; @Getter private final Label warningIcon; + @Getter + private final Label privacyIcon; + private Label currentIcon; private PopOver popover; private boolean hidePopover; @@ -63,14 +66,19 @@ public InfoInputTextField() { warningIcon.setLayoutY(3); warningIcon.getStyleClass().addAll("icon", "warning"); + privacyIcon = getIcon(AwesomeIcon.EYE_CLOSE); + privacyIcon.setLayoutY(3); + privacyIcon.getStyleClass().addAll("icon", "info"); + AnchorPane.setLeftAnchor(infoIcon, 7.0); AnchorPane.setLeftAnchor(warningIcon, 7.0); + AnchorPane.setLeftAnchor(privacyIcon, 7.0); AnchorPane.setRightAnchor(inputTextField, 0.0); AnchorPane.setLeftAnchor(inputTextField, 0.0); hideIcons(); - getChildren().addAll(inputTextField, infoIcon, warningIcon); + getChildren().addAll(inputTextField, infoIcon, warningIcon, privacyIcon); } private void hideIcons() { @@ -78,6 +86,8 @@ private void hideIcons() { infoIcon.setVisible(false); warningIcon.setManaged(false); warningIcon.setVisible(false); + privacyIcon.setManaged(false); + privacyIcon.setVisible(false); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -98,13 +108,22 @@ public void setContentForWarningPopOver(Node node) { setActionHandlers(node); } + public void setContentForPrivacyPopOver(Node node) { + currentIcon = privacyIcon; + + hideIcons(); + setActionHandlers(node); + } + public void setIconsRightAligned() { AnchorPane.clearConstraints(infoIcon); AnchorPane.clearConstraints(warningIcon); + AnchorPane.clearConstraints(privacyIcon); AnchorPane.clearConstraints(inputTextField); AnchorPane.setRightAnchor(infoIcon, 7.0); AnchorPane.setRightAnchor(warningIcon, 7.0); + AnchorPane.setRightAnchor(privacyIcon, 7.0); AnchorPane.setLeftAnchor(inputTextField, 0.0); AnchorPane.setRightAnchor(inputTextField, 0.0); } diff --git a/src/main/java/bisq/desktop/components/InfoTextField.java b/src/main/java/bisq/desktop/components/InfoTextField.java index 27b00d73812..fc5c4f95898 100644 --- a/src/main/java/bisq/desktop/components/InfoTextField.java +++ b/src/main/java/bisq/desktop/components/InfoTextField.java @@ -19,7 +19,6 @@ import bisq.common.UserThread; -import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; import org.controlsfx.control.PopOver; @@ -37,71 +36,123 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import lombok.Getter; + +import static bisq.desktop.util.FormBuilder.getIcon; + public class InfoTextField extends AnchorPane { public static final Logger log = LoggerFactory.getLogger(InfoTextField.class); + @Getter + protected final TextField textField; + private final StringProperty text = new SimpleStringProperty(); protected final Label infoIcon; - protected final TextField textField; + protected final Label privacyIcon; + private Label currentIcon; private Boolean hidePopover; - private PopOver infoPopover; + private PopOver popover; + private PopOver.ArrowLocation arrowLocation; public InfoTextField() { + + arrowLocation = PopOver.ArrowLocation.RIGHT_TOP;; textField = new TextField(); textField.setEditable(false); textField.textProperty().bind(text); textField.setFocusTraversable(false); - infoIcon = new Label(); + infoIcon = getIcon(AwesomeIcon.INFO_SIGN); infoIcon.setLayoutY(3); infoIcon.getStyleClass().addAll("icon", "info"); - AwesomeDude.setIcon(infoIcon, AwesomeIcon.INFO_SIGN); + + privacyIcon = getIcon(AwesomeIcon.EYE_CLOSE); + privacyIcon.setLayoutY(3); + privacyIcon.getStyleClass().addAll("icon", "info"); AnchorPane.setRightAnchor(infoIcon, 7.0); + AnchorPane.setRightAnchor(privacyIcon, 7.0); AnchorPane.setRightAnchor(textField, 0.0); AnchorPane.setLeftAnchor(textField, 0.0); - getChildren().addAll(textField, infoIcon); + hideIcons(); + + getChildren().addAll(textField, infoIcon, privacyIcon); } + + /////////////////////////////////////////////////////////////////////////////////////////// // Public /////////////////////////////////////////////////////////////////////////////////////////// public void setContentForInfoPopOver(Node node) { + + currentIcon = infoIcon; + + hideIcons(); + } + + public void setContentForPrivacyPopOver(Node node) { + currentIcon = privacyIcon; + + hideIcons(); + setActionHandlers(node); + } + + public void setIconsLeftAligned() { + arrowLocation = PopOver.ArrowLocation.LEFT_TOP;; + + AnchorPane.clearConstraints(infoIcon); + AnchorPane.clearConstraints(privacyIcon); + + AnchorPane.setLeftAnchor(infoIcon, 7.0); + AnchorPane.setLeftAnchor(privacyIcon, 7.0); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + private void hideIcons() { + infoIcon.setManaged(false); + infoIcon.setVisible(false); + privacyIcon.setManaged(false); + privacyIcon.setVisible(false); + } + + private void setActionHandlers(Node node) { + + currentIcon.setManaged(true); + currentIcon.setVisible(true); + // As we don't use binding here we need to recreate it on mouse over to reflect the current state - infoIcon.setOnMouseEntered(e -> { + currentIcon.setOnMouseEntered(e -> { hidePopover = false; - showInfoPopOver(node); + showPopOver(node); }); - infoIcon.setOnMouseExited(e -> { - if (infoPopover != null) - infoPopover.hide(); + currentIcon.setOnMouseExited(e -> { + if (popover != null) + popover.hide(); hidePopover = true; UserThread.runAfter(() -> { if (hidePopover) { - infoPopover.hide(); + popover.hide(); hidePopover = false; } }, 250, TimeUnit.MILLISECONDS); }); } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void showInfoPopOver(Node node) { + private void showPopOver(Node node) { node.getStyleClass().add("default-text"); - infoPopover = new PopOver(node); - if (infoIcon.getScene() != null) { - infoPopover.setDetachable(false); - infoPopover.setArrowLocation(PopOver.ArrowLocation.RIGHT_TOP); - infoPopover.setArrowIndent(5); + popover = new PopOver(node); + if (currentIcon.getScene() != null) { + popover.setDetachable(false); + popover.setArrowLocation(arrowLocation); + popover.setArrowIndent(5); - infoPopover.show(infoIcon, -17); + popover.show(currentIcon, -17); } } diff --git a/src/main/java/bisq/desktop/main/offer/MutableOfferView.java b/src/main/java/bisq/desktop/main/offer/MutableOfferView.java index eca630078d0..1e47b73ffa9 100644 --- a/src/main/java/bisq/desktop/main/offer/MutableOfferView.java +++ b/src/main/java/bisq/desktop/main/offer/MutableOfferView.java @@ -50,6 +50,7 @@ import bisq.desktop.util.Layout; import bisq.desktop.util.Transitions; +import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; import bisq.core.offer.Offer; @@ -158,7 +159,8 @@ public abstract class MutableOfferView extends private ChangeListener amountFocusedListener, minAmountFocusedListener, volumeFocusedListener, buyerSecurityDepositFocusedListener, priceFocusedListener, placeOfferCompletedListener, priceAsPercentageFocusedListener; - private ChangeListener tradeCurrencyCodeListener, errorMessageListener, marketPriceMarginListener; + private ChangeListener tradeCurrencyCodeListener, errorMessageListener, + marketPriceMarginListener, volumeListener; private ChangeListener marketPriceAvailableListener; private EventHandler currencyComboBoxSelectionHandler, paymentAccountsComboBoxSelectionHandler; private OfferView.CloseHandler closeHandler; @@ -167,7 +169,7 @@ public abstract class MutableOfferView extends private final List editOfferElements = new ArrayList<>(); private boolean clearXchangeWarningDisplayed, isActivated; private ChangeListener getShowWalletFundedNotificationListener; - private InfoInputTextField marketBasedPriceInfoInputTextField; + private InfoInputTextField marketBasedPriceInfoInputTextField, volumeInfoInputTextField; protected TitledGroupBg amountTitledGroupBg; /////////////////////////////////////////////////////////////////////////////////////////// @@ -718,6 +720,12 @@ private void createListeners() { } }; + volumeListener = (observable, oldValue, newValue) -> { + if (!newValue.equals("") && CurrencyUtil.isFiatCurrency(model.tradeCurrencyCode.get())) { + volumeInfoInputTextField.setContentForPrivacyPopOver(createPopoverLabel(Res.get("offerbook.info.roundedFiatVolume"))); + } + }; + marketPriceMarginListener = (observable, oldValue, newValue) -> { if (marketBasedPriceInfoInputTextField != null) { String tooltip; @@ -776,6 +784,7 @@ private void addListeners() { model.tradeCurrencyCode.addListener(tradeCurrencyCodeListener); model.marketPriceAvailableProperty.addListener(marketPriceAvailableListener); model.marketPriceMargin.addListener(marketPriceMarginListener); + model.volume.addListener(volumeListener); // focus out amountTextField.focusedProperty().addListener(amountFocusedListener); @@ -803,6 +812,7 @@ private void removeListeners() { model.tradeCurrencyCode.removeListener(tradeCurrencyCodeListener); model.marketPriceAvailableProperty.removeListener(marketPriceAvailableListener); model.marketPriceMargin.removeListener(marketPriceMarginListener); + model.volume.removeListener(volumeListener); // focus out amountTextField.focusedProperty().removeListener(amountFocusedListener); @@ -1180,9 +1190,10 @@ private void addAmountPriceFields() { resultLabel.setPadding(new Insets(14, 2, 0, 2)); // volume - Tuple3 volumeValueCurrencyBoxTuple = getEditableValueCurrencyBox(Res.get("createOffer.volume.prompt")); + Tuple3 volumeValueCurrencyBoxTuple = getEditableValueCurrencyBoxWithInfo(Res.get("createOffer.volume.prompt")); HBox volumeValueCurrencyBox = volumeValueCurrencyBoxTuple.first; - volumeTextField = volumeValueCurrencyBoxTuple.second; + volumeInfoInputTextField = volumeValueCurrencyBoxTuple.second; + volumeTextField = volumeInfoInputTextField.getInputTextField(); editOfferElements.add(volumeTextField); volumeCurrencyLabel = volumeValueCurrencyBoxTuple.third; editOfferElements.add(volumeCurrencyLabel); diff --git a/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java b/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java index 6fa8de65f3e..711ae5b6558 100644 --- a/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java @@ -26,6 +26,7 @@ import bisq.desktop.components.BalanceTextField; import bisq.desktop.components.BusyAnimation; import bisq.desktop.components.FundsTextField; +import bisq.desktop.components.InfoTextField; import bisq.desktop.components.InputTextField; import bisq.desktop.components.TitledGroupBg; import bisq.desktop.main.MainView; @@ -49,6 +50,7 @@ import bisq.desktop.util.GUIUtil; import bisq.desktop.util.Layout; +import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; @@ -111,6 +113,7 @@ import static bisq.desktop.util.FormBuilder.addLabelFundsTextfield; import static bisq.desktop.util.FormBuilder.getAmountCurrencyBox; import static bisq.desktop.util.FormBuilder.getNonEditableValueCurrencyBox; +import static bisq.desktop.util.FormBuilder.getNonEditableValueCurrencyBoxWithInfo; import static javafx.beans.binding.Bindings.createStringBinding; @FxmlView @@ -152,6 +155,7 @@ public class TakeOfferView extends ActivatableViewAndModel getShowWalletFundedNotificationListener; + private InfoTextField volumeInfoTextField; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle @@ -324,6 +328,9 @@ public void initWithData(Offer offer) { addressTextField.setPaymentLabel(model.getPaymentLabel()); addressTextField.setAddress(model.dataModel.getAddressEntry().getAddressString()); + if (CurrencyUtil.isFiatCurrency(offer.getCurrencyCode())) + volumeInfoTextField.setContentForPrivacyPopOver(createPopoverLabel(Res.get("offerbook.info.roundedFiatVolume"))); + if (offer.getPrice() == null) new Popup<>().warning(Res.get("takeOffer.noPriceFeedAvailable")) .onClose(this::close) @@ -496,7 +503,7 @@ private void close() { private void addBindings() { amountTextField.textProperty().bindBidirectional(model.amount); - volumeTextField.textProperty().bindBidirectional(model.volume); + volumeInfoTextField.textProperty().bindBidirectional(model.volume); totalToPayTextField.textProperty().bind(model.totalToPay); addressTextField.amountAsCoinProperty().bind(model.dataModel.getMissingCoin()); amountTextField.validationResultProperty().bind(model.amountValidationResult); @@ -515,7 +522,7 @@ private void addBindings() { private void removeBindings() { amountTextField.textProperty().unbindBidirectional(model.amount); - volumeTextField.textProperty().unbindBidirectional(model.volume); + volumeInfoTextField.textProperty().unbindBidirectional(model.volume); totalToPayTextField.textProperty().unbind(); addressTextField.amountAsCoinProperty().unbind(); amountTextField.validationResultProperty().unbind(); @@ -941,9 +948,11 @@ private void addAmountPriceFields() { resultLabel.setPadding(new Insets(14, 2, 0, 2)); // volume - Tuple3 volumeValueCurrencyBoxTuple = getNonEditableValueCurrencyBox(); + Tuple3 volumeValueCurrencyBoxTuple = getNonEditableValueCurrencyBoxWithInfo(); HBox volumeValueCurrencyBox = volumeValueCurrencyBoxTuple.first; - volumeTextField = volumeValueCurrencyBoxTuple.second; + + volumeInfoTextField = volumeValueCurrencyBoxTuple.second; + volumeTextField = volumeInfoTextField.getTextField(); volumeCurrencyLabel = volumeValueCurrencyBoxTuple.third; Tuple2 volumeInputBoxTuple = getTradeInputBox(volumeValueCurrencyBox, model.volumeDescriptionLabel.get()); volumeDescriptionLabel = volumeInputBoxTuple.first; @@ -1069,6 +1078,14 @@ private GridPane createInfoPopover() { return infoGridPane; } + private Label createPopoverLabel(String text) { + final Label label = new Label(text); + label.setPrefWidth(300); + label.setWrapText(true); + label.setPadding(new Insets(10)); + return label; + } + private void addPayInfoEntry(GridPane infoGridPane, int row, String labelText, String value) { Label label = new AutoTooltipLabel(labelText); TextField textField = new TextField(value); diff --git a/src/main/java/bisq/desktop/util/FormBuilder.java b/src/main/java/bisq/desktop/util/FormBuilder.java index 942eb13082c..3f31dd071c8 100644 --- a/src/main/java/bisq/desktop/util/FormBuilder.java +++ b/src/main/java/bisq/desktop/util/FormBuilder.java @@ -1203,6 +1203,25 @@ public static Tuple3 getNonEditableValueCurrencyBox() { return new Tuple3<>(box, textField, currency); } + public static Tuple3 getNonEditableValueCurrencyBoxWithInfo() { + InfoTextField infoTextField = new InfoTextField(); + infoTextField.setIconsLeftAligned(); + TextField textField = infoTextField.getTextField(); + textField.setPrefWidth(190); + textField.setAlignment(Pos.CENTER_RIGHT); + textField.setId("text-input-with-currency-text-field"); + textField.setMouseTransparent(true); + textField.setEditable(false); + textField.setFocusTraversable(false); + + Label currency = new AutoTooltipLabel(Res.getBaseCurrencyCode()); + currency.setId("currency-info-label-disabled"); + + HBox box = new HBox(); + box.getChildren().addAll(infoTextField, currency); + return new Tuple3<>(box, infoTextField, currency); + } + public static Tuple3 getAmountCurrencyBox(String promptText) { InputTextField input = new InputTextField(); input.setPrefWidth(190);