From b6e8f66553f5dac2fa99ae47ae4f3e65dd62b9ca Mon Sep 17 00:00:00 2001 From: xyzmaker123 Date: Sun, 20 Jun 2021 12:08:17 +0200 Subject: [PATCH 01/11] Tooltip for unsigned accounts needs to provide more information --- .../witness/AccountAgeWitnessService.java | 4 + .../resources/i18n/displayStrings.properties | 8 + desktop/src/main/java/bisq/desktop/bisq.css | 9 + .../components/AccountStatusTooltipLabel.java | 139 ++++++++++++++++ .../offer/offerbook/OfferBookListItem.java | 156 ++++++++++-------- .../main/offer/offerbook/OfferBookView.java | 13 +- 6 files changed, 249 insertions(+), 80 deletions(-) create mode 100644 desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index 4a5bc8f1658..85d35e19af1 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -131,6 +131,10 @@ public String getDisplayString() { return String.format(displayString, daysUntilLimitLifted); } + public boolean isLimitLifted() { + return this == PEER_LIMIT_LIFTED || this == PEER_SIGNER || this == ARBITRATOR; + } + } private final KeyRing keyRing; diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index f4c5ab238a3..a61c927043b 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -354,6 +354,14 @@ offerbook.timeSinceSigning.info.signer=signed by peer and can sign peer accounts offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} days offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing +offerbook.timeSinceSigning.tooltip.accountLimit=Account limit: {0} +offerbook.timeSinceSigning.tooltip.accountLimitLifted=Account limit lifted +offerbook.timeSinceSigning.tooltip.info.unsigned=This account hasn't been signed yet +offerbook.timeSinceSigning.tooltip.info.signed=This account has been signed +offerbook.timeSinceSigning.tooltip.info.signedAndLifted=This account has been signed and can sign peer accounts +offerbook.timeSinceSigning.tooltip.checkmark.buyBtc=but BTC from a signed account +offerbook.timeSinceSigning.tooltip.checkmark.wait=wait a minimum of {0} days +offerbook.timeSinceSigning.tooltip.learnMore=Learn more offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n\ diff --git a/desktop/src/main/java/bisq/desktop/bisq.css b/desktop/src/main/java/bisq/desktop/bisq.css index 87d187683a3..198efc443ee 100644 --- a/desktop/src/main/java/bisq/desktop/bisq.css +++ b/desktop/src/main/java/bisq/desktop/bisq.css @@ -31,6 +31,10 @@ -fx-text-fill: -bs-rd-error-red; } +.text-gray-ddd { + -fx-text-fill: -bs-color-gray-ddd; +} + .error { -fx-accent: -bs-rd-error-red; } @@ -609,6 +613,11 @@ tree-table-view:focused { -fx-font-size: 13; } +.bold-text, +.bold-text .text { + -fx-font-weight: bold; +} + /* Splash */ #splash { -fx-background-color: -bs-background-color; diff --git a/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java new file mode 100644 index 00000000000..6b1b26d359c --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java @@ -0,0 +1,139 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.components; + +import bisq.desktop.components.controlsfx.control.PopOver; +import bisq.desktop.main.offer.offerbook.OfferBookListItem; +import bisq.desktop.util.FormBuilder; +import bisq.desktop.util.GUIUtil; + +import bisq.core.account.sign.SignedWitnessService; +import bisq.core.locale.Res; +import bisq.core.offer.OfferRestrictions; +import bisq.core.util.coin.CoinFormatter; + +import de.jensd.fx.fontawesome.AwesomeIcon; + +import javafx.scene.Node; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; + + +public class AccountStatusTooltipLabel extends AutoTooltipLabel { + + public static final int DEFAULT_WIDTH = 300; + private final Node textIcon; + private final PopOverWrapper popoverWrapper = new PopOverWrapper(); + private final OfferBookListItem.WitnessAgeData witnessAgeData; + private final String popupTitle; + + public AccountStatusTooltipLabel(OfferBookListItem.WitnessAgeData witnessAgeData, + CoinFormatter formatter) { + super(witnessAgeData.getDisplayString()); + this.witnessAgeData = witnessAgeData; + this.textIcon = FormBuilder.getIcon(witnessAgeData.getIcon()); + this.popupTitle = witnessAgeData.isLimitLifted() + ? Res.get("offerbook.timeSinceSigning.tooltip.accountLimitLifted") + : Res.get("offerbook.timeSinceSigning.tooltip.accountLimit", formatter.formatCoinWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT)); + + positionAndActivateIcon(); + } + + private void positionAndActivateIcon() { + textIcon.setOpacity(0.4); + textIcon.getStyleClass().add("tooltip-icon"); + textIcon.setOnMouseEntered(e -> popoverWrapper.showPopOver(() -> createPopOver())); + textIcon.setOnMouseExited(e -> popoverWrapper.hidePopOver()); + + setGraphic(textIcon); + setContentDisplay(ContentDisplay.RIGHT); + } + + private PopOver createPopOver() { + Label titleLabel = new Label(popupTitle); + titleLabel.setMaxWidth(DEFAULT_WIDTH); + titleLabel.setWrapText(true); + titleLabel.setPadding(new Insets(10, 10, 2, 10)); + titleLabel.getStyleClass().add("bold-text"); + titleLabel.getStyleClass().add("default-text"); + + Label infoLabel = new Label(witnessAgeData.getInfo()); + infoLabel.setMaxWidth(DEFAULT_WIDTH); + infoLabel.setWrapText(true); + infoLabel.setPadding(new Insets(2, 10, 2, 10)); + infoLabel.getStyleClass().add("default-text"); + + Label buyLabel = createDetailsItem( + Res.get("offerbook.timeSinceSigning.tooltip.checkmark.buyBtc"), + witnessAgeData.isAccountSigned() + ); + Label waitLabel = createDetailsItem( + Res.get("offerbook.timeSinceSigning.tooltip.checkmark.wait", SignedWitnessService.SIGNER_AGE_DAYS), + witnessAgeData.isLimitLifted() + ); + + Hyperlink learnMoreLink = new Hyperlink(Res.get("offerbook.timeSinceSigning.tooltip.learnMore")); + learnMoreLink.setMaxWidth(DEFAULT_WIDTH); + learnMoreLink.setWrapText(true); + learnMoreLink.setPadding(new Insets(10, 10, 2, 10)); + learnMoreLink.getStyleClass().add("very-small-text"); + learnMoreLink.setOnAction((e) -> { + GUIUtil.openWebPage("https://bisq.wiki/Account_limits"); + }); + + VBox vBox = new VBox(2, titleLabel, infoLabel, buyLabel, waitLabel, learnMoreLink); + vBox.setPadding(new Insets(2, 0, 2, 0)); + vBox.setAlignment(Pos.CENTER_LEFT); + + PopOver popOver = new PopOver(vBox); + if (textIcon.getScene() != null) { + popOver.setDetachable(false); + popOver.setArrowLocation(PopOver.ArrowLocation.LEFT_CENTER); + popOver.show(textIcon, -10); + } + return popOver; + } + + private Label createDetailsItem(String text, boolean active) { + Label icon = FormBuilder.getIcon(active ? AwesomeIcon.OK_SIGN : AwesomeIcon.REMOVE_SIGN); + icon.setLayoutY(4); + icon.getStyleClass().add("icon"); + if (active) { + icon.getStyleClass().add("highlight"); + } else { + icon.getStyleClass().add("text-gray-ddd"); + } + + Label label = new Label(text, icon); + label.setMaxWidth(DEFAULT_WIDTH); + label.setWrapText(true); + label.setPadding(new Insets(0, 10, 0, 10)); + label.getStyleClass().addAll("small-text"); + if (active) { + label.getStyleClass().add("success-text"); + } else { + label.getStyleClass().add("text-gray-ddd"); + } + return label; + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookListItem.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookListItem.java index 4d389239935..1472e55f0ec 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookListItem.java @@ -38,6 +38,8 @@ import lombok.Value; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + @Slf4j public class OfferBookListItem { @@ -55,102 +57,112 @@ public OfferBookListItem(Offer offer) { public WitnessAgeData getWitnessAgeData(AccountAgeWitnessService accountAgeWitnessService, SignedWitnessService signedWitnessService) { if (witnessAgeData == null) { - long ageInMs; - long daysSinceSignedAsLong = -1; - long accountAgeDaysAsLong = -1; - long accountAgeDaysNotYetSignedAsLong = -1; - String displayString; - String info; - GlyphIcons icon; - if (CurrencyUtil.isCryptoCurrency(offer.getCurrencyCode())) { - // Altcoins - displayString = Res.get("offerbook.timeSinceSigning.notSigned.noNeed"); - info = Res.get("shared.notSigned.noNeedAlts"); - icon = MaterialDesignIcon.INFORMATION_OUTLINE; + witnessAgeData = new WitnessAgeData(WitnessAgeData.TYPE_ALTCOINS); } else if (PaymentMethod.hasChargebackRisk(offer.getPaymentMethod(), offer.getCurrencyCode())) { // Fiat and signed witness required Optional optionalWitness = accountAgeWitnessService.findWitness(offer); - AccountAgeWitnessService.SignState signState = optionalWitness.map(accountAgeWitnessService::getSignState) + AccountAgeWitnessService.SignState signState = optionalWitness + .map(accountAgeWitnessService::getSignState) .orElse(AccountAgeWitnessService.SignState.UNSIGNED); - boolean isSignedAccountAgeWitness = optionalWitness.map(signedWitnessService::isSignedAccountAgeWitness) + + boolean isSignedAccountAgeWitness = optionalWitness + .map(signedWitnessService::isSignedAccountAgeWitness) .orElse(false); + if (isSignedAccountAgeWitness || !signState.equals(AccountAgeWitnessService.SignState.UNSIGNED)) { // either signed & limits lifted, or waiting for limits to be lifted // Or banned - daysSinceSignedAsLong = TimeUnit.MILLISECONDS.toDays(optionalWitness.map(witness -> - accountAgeWitnessService.getWitnessSignAge(witness, new Date())) - .orElse(0L)); - displayString = Res.get("offerbook.timeSinceSigning.daysSinceSigning", daysSinceSignedAsLong); - info = Res.get("offerbook.timeSinceSigning.info", signState.getDisplayString()); + witnessAgeData = new WitnessAgeData( + signState.isLimitLifted() ? WitnessAgeData.TYPE_SIGNED_AND_LIMIT_LIFTED : WitnessAgeData.TYPE_SIGNED_OR_BANNED, + optionalWitness.map(witness -> accountAgeWitnessService.getWitnessSignAge(witness, new Date())).orElse(0L), + signState); } else { - // Unsigned case - ageInMs = optionalWitness.map(e -> accountAgeWitnessService.getAccountAge(e, new Date())) - .orElse(-1L); - accountAgeDaysNotYetSignedAsLong = ageInMs > -1 ? TimeUnit.MILLISECONDS.toDays(ageInMs) : 0; - displayString = Res.get("offerbook.timeSinceSigning.notSigned"); - info = Res.get("shared.notSigned", accountAgeDaysNotYetSignedAsLong); + witnessAgeData = new WitnessAgeData( + WitnessAgeData.TYPE_NOT_SIGNED, + optionalWitness.map(e -> accountAgeWitnessService.getAccountAge(e, new Date())).orElse(0L), + signState + ); } - - icon = GUIUtil.getIconForSignState(signState); } else { // Fiat, no signed witness required, we show account age - ageInMs = accountAgeWitnessService.getAccountAge(offer); - accountAgeDaysAsLong = ageInMs > -1 ? TimeUnit.MILLISECONDS.toDays(ageInMs) : 0; - displayString = Res.get("offerbook.timeSinceSigning.notSigned.ageDays", accountAgeDaysAsLong); - info = Res.get("shared.notSigned.noNeedDays", accountAgeDaysAsLong); - icon = MaterialDesignIcon.CHECKBOX_MARKED_OUTLINE; + witnessAgeData = new WitnessAgeData( + WitnessAgeData.TYPE_NOT_SIGNING_REQUIRED, + accountAgeWitnessService.getAccountAge(offer) + ); } - - witnessAgeData = new WitnessAgeData(displayString, info, icon, daysSinceSignedAsLong, accountAgeDaysNotYetSignedAsLong, accountAgeDaysAsLong); } return witnessAgeData; } @Value - public static class WitnessAgeData { - private final String displayString; - private final String info; - private final GlyphIcons icon; - private final Long daysSinceSignedAsLong; - private final long accountAgeDaysNotYetSignedAsLong; - private final Long accountAgeDaysAsLong; + public static class WitnessAgeData implements Comparable { + String displayString; + String info; + GlyphIcons icon; // Used for sorting - private final Long type; + Long type; // Used for sorting - private final Long days; - - public WitnessAgeData(String displayString, - String info, - GlyphIcons icon, - long daysSinceSignedAsLong, - long accountAgeDaysNotYetSignedAsLong, - long accountAgeDaysAsLong) { - this.displayString = displayString; - this.info = info; - this.icon = icon; - this.daysSinceSignedAsLong = daysSinceSignedAsLong; - this.accountAgeDaysNotYetSignedAsLong = accountAgeDaysNotYetSignedAsLong; - this.accountAgeDaysAsLong = accountAgeDaysAsLong; - - if (daysSinceSignedAsLong > -1) { - // First we show signed accounts sorted by days - this.type = 3L; - this.days = daysSinceSignedAsLong; - } else if (accountAgeDaysNotYetSignedAsLong > -1) { - // Next group is not yet signed accounts sorted by account age - this.type = 2L; - this.days = accountAgeDaysNotYetSignedAsLong; - } else if (accountAgeDaysAsLong > -1) { - // Next group is not signing required accounts sorted by account age - this.type = 1L; - this.days = accountAgeDaysAsLong; + Long days; + + public static final long TYPE_SIGNED_AND_LIMIT_LIFTED = 4L; + public static final long TYPE_SIGNED_OR_BANNED = 3L; + public static final long TYPE_NOT_SIGNED = 2L; + public static final long TYPE_NOT_SIGNING_REQUIRED = 1L; + public static final long TYPE_ALTCOINS = 0L; + + public WitnessAgeData(long type) { + this(type, 0, null); + } + + public WitnessAgeData(long type, long days) { + this(type, days, null); + } + + public WitnessAgeData(long type, long age, AccountAgeWitnessService.SignState signState) { + this.type = type; + long days = age > -1 ? TimeUnit.MILLISECONDS.toDays(age) : 0; + this.days = days; + + if (type == TYPE_SIGNED_AND_LIMIT_LIFTED) { + this.displayString = Res.get("offerbook.timeSinceSigning.daysSinceSigning", days); + this.info = Res.get("offerbook.timeSinceSigning.tooltip.info.signedAndLifted"); + this.icon = GUIUtil.getIconForSignState(signState); + } else if (type == TYPE_SIGNED_OR_BANNED) { + this.displayString = Res.get("offerbook.timeSinceSigning.daysSinceSigning", days); + this.info = Res.get("offerbook.timeSinceSigning.tooltip.info.signed"); + this.icon = GUIUtil.getIconForSignState(signState); + } else if (type == TYPE_NOT_SIGNED) { + this.displayString = Res.get("offerbook.timeSinceSigning.notSigned"); + this.info = Res.get("offerbook.timeSinceSigning.tooltip.info.unsigned"); + this.icon = GUIUtil.getIconForSignState(signState); + } else if (type == TYPE_NOT_SIGNING_REQUIRED) { + this.displayString = Res.get("offerbook.timeSinceSigning.notSigned.ageDays", days); + this.info = Res.get("shared.notSigned.noNeedDays", days); + this.icon = MaterialDesignIcon.CHECKBOX_MARKED_OUTLINE; } else { - // No signing and age required (altcoins) - this.type = 0L; - this.days = 0L; + this.displayString = Res.get("offerbook.timeSinceSigning.notSigned.noNeed"); + this.info = Res.get("shared.notSigned.noNeedAlts"); + this.icon = MaterialDesignIcon.INFORMATION_OUTLINE; } } + + public boolean isAccountSigned() { + return this.type == TYPE_SIGNED_AND_LIMIT_LIFTED || this.type == TYPE_SIGNED_OR_BANNED; + } + + public boolean isLimitLifted() { + return this.type == TYPE_SIGNED_AND_LIMIT_LIFTED; + } + + public boolean isSigningRequired() { + return this.type != TYPE_NOT_SIGNING_REQUIRED && this.type != TYPE_ALTCOINS; + } + + @Override + public int compareTo(@NotNull WitnessAgeData o) { + return (int) (this.type.equals(o.getType()) ? this.days - o.getDays() : this.type - o.getType()); + } } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index e109d4b7ea2..75f4e45ae7b 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -20,6 +20,7 @@ import bisq.desktop.Navigation; import bisq.desktop.common.view.ActivatableViewAndModel; import bisq.desktop.common.view.FxmlView; +import bisq.desktop.components.AccountStatusTooltipLabel; import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipLabel; import bisq.desktop.components.AutoTooltipSlideToggleButton; @@ -290,10 +291,7 @@ public void initialize() { }, Comparator.nullsFirst(Comparator.naturalOrder()))); - Comparator comparator = Comparator.comparing(e -> e.getWitnessAgeData(accountAgeWitnessService, signedWitnessService).getType(), Comparator.nullsFirst(Comparator.naturalOrder())); - signingStateColumn.setComparator(comparator. - thenComparing(e -> e.getWitnessAgeData(accountAgeWitnessService, signedWitnessService).getDays(), - Comparator.nullsFirst(Comparator.naturalOrder()))); + signingStateColumn.setComparator(Comparator.comparing(e -> e.getWitnessAgeData(accountAgeWitnessService, signedWitnessService), Comparator.nullsFirst(Comparator.naturalOrder()))); nrOfOffersLabel = new AutoTooltipLabel(""); nrOfOffersLabel.setId("num-offers"); @@ -1139,10 +1137,9 @@ public void updateItem(final OfferBookListItem item, boolean empty) { if (item != null && !empty) { var witnessAgeData = item.getWitnessAgeData(accountAgeWitnessService, signedWitnessService); - InfoAutoTooltipLabel label = new InfoAutoTooltipLabel(witnessAgeData.getDisplayString(), - witnessAgeData.getIcon(), - ContentDisplay.RIGHT, - witnessAgeData.getInfo()); + var label = witnessAgeData.isSigningRequired() + ? new AccountStatusTooltipLabel(witnessAgeData, formatter) + : new InfoAutoTooltipLabel(witnessAgeData.getDisplayString(), witnessAgeData.getIcon(), ContentDisplay.RIGHT, witnessAgeData.getInfo()); setGraphic(label); } else { setGraphic(null); From 6bcd22bd558fa9434ca9c02c4816d8f1b5e840d3 Mon Sep 17 00:00:00 2001 From: xyzmaker123 <84982606+xyzmaker123@users.noreply.github.com> Date: Thu, 24 Jun 2021 17:30:12 +0200 Subject: [PATCH 02/11] Update desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java Co-authored-by: Christoph Atteneder --- .../java/bisq/desktop/components/AccountStatusTooltipLabel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java index 6b1b26d359c..6179a481e5e 100644 --- a/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java +++ b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java @@ -62,7 +62,7 @@ public AccountStatusTooltipLabel(OfferBookListItem.WitnessAgeData witnessAgeData private void positionAndActivateIcon() { textIcon.setOpacity(0.4); textIcon.getStyleClass().add("tooltip-icon"); - textIcon.setOnMouseEntered(e -> popoverWrapper.showPopOver(() -> createPopOver())); + textIcon.setOnMouseEntered(e -> popoverWrapper.showPopOver(this::createPopOver)); textIcon.setOnMouseExited(e -> popoverWrapper.hidePopOver()); setGraphic(textIcon); From 54b93a4411c79d7623331257eddc4eedc74d558e Mon Sep 17 00:00:00 2001 From: xyzmaker123 <84982606+xyzmaker123@users.noreply.github.com> Date: Thu, 24 Jun 2021 17:30:26 +0200 Subject: [PATCH 03/11] Update desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java Co-authored-by: Christoph Atteneder --- .../bisq/desktop/components/AccountStatusTooltipLabel.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java index 6179a481e5e..68fe7b1caf3 100644 --- a/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java +++ b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java @@ -97,9 +97,7 @@ private PopOver createPopOver() { learnMoreLink.setWrapText(true); learnMoreLink.setPadding(new Insets(10, 10, 2, 10)); learnMoreLink.getStyleClass().add("very-small-text"); - learnMoreLink.setOnAction((e) -> { - GUIUtil.openWebPage("https://bisq.wiki/Account_limits"); - }); + learnMoreLink.setOnAction((e) -> GUIUtil.openWebPage("https://bisq.wiki/Account_limits")); VBox vBox = new VBox(2, titleLabel, infoLabel, buyLabel, waitLabel, learnMoreLink); vBox.setPadding(new Insets(2, 0, 2, 0)); From 5a2592c8322b8ba1fc9b1e9d17265b2787b3450e Mon Sep 17 00:00:00 2001 From: xyzmaker123 <84982606+xyzmaker123@users.noreply.github.com> Date: Thu, 24 Jun 2021 17:30:34 +0200 Subject: [PATCH 04/11] Update core/src/main/resources/i18n/displayStrings.properties Co-authored-by: Christoph Atteneder --- core/src/main/resources/i18n/displayStrings.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index a61c927043b..e5551ef6174 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -359,7 +359,7 @@ offerbook.timeSinceSigning.tooltip.accountLimitLifted=Account limit lifted offerbook.timeSinceSigning.tooltip.info.unsigned=This account hasn't been signed yet offerbook.timeSinceSigning.tooltip.info.signed=This account has been signed offerbook.timeSinceSigning.tooltip.info.signedAndLifted=This account has been signed and can sign peer accounts -offerbook.timeSinceSigning.tooltip.checkmark.buyBtc=but BTC from a signed account +offerbook.timeSinceSigning.tooltip.checkmark.buyBtc=buy BTC from a signed account offerbook.timeSinceSigning.tooltip.checkmark.wait=wait a minimum of {0} days offerbook.timeSinceSigning.tooltip.learnMore=Learn more offerbook.xmrAutoConf=Is auto-confirm enabled From 5458dbe90e09fc233a336e93a857cc5089ccdb96 Mon Sep 17 00:00:00 2001 From: xyzmaker123 Date: Thu, 24 Jun 2021 17:37:37 +0200 Subject: [PATCH 05/11] Update class name --- desktop/src/main/java/bisq/desktop/bisq.css | 8 ++++---- .../desktop/components/AccountStatusTooltipLabel.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/bisq.css b/desktop/src/main/java/bisq/desktop/bisq.css index 198efc443ee..49844e0c41b 100644 --- a/desktop/src/main/java/bisq/desktop/bisq.css +++ b/desktop/src/main/java/bisq/desktop/bisq.css @@ -31,10 +31,6 @@ -fx-text-fill: -bs-rd-error-red; } -.text-gray-ddd { - -fx-text-fill: -bs-color-gray-ddd; -} - .error { -fx-accent: -bs-rd-error-red; } @@ -1925,6 +1921,10 @@ textfield */ -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, -1, 3); } +.account-status-inactive-info-item { + -fx-text-fill: -bs-color-gray-ddd; +} + #price-feed-combo { -fx-background-color: none; } diff --git a/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java index 68fe7b1caf3..33559b77325 100644 --- a/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java +++ b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java @@ -119,7 +119,7 @@ private Label createDetailsItem(String text, boolean active) { if (active) { icon.getStyleClass().add("highlight"); } else { - icon.getStyleClass().add("text-gray-ddd"); + icon.getStyleClass().add("account-status-inactive-info-item"); } Label label = new Label(text, icon); @@ -130,7 +130,7 @@ private Label createDetailsItem(String text, boolean active) { if (active) { label.getStyleClass().add("success-text"); } else { - label.getStyleClass().add("text-gray-ddd"); + label.getStyleClass().add("account-status-inactive-info-item"); } return label; } From 31b08618fd121822bf093a5aee1e586939209576 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 16 Jul 2021 09:23:12 +0200 Subject: [PATCH 06/11] Update PopOver component --- .../controlsfx/control/PopOver.java | 473 ++++++++++++++---- .../components/controlsfx/control/popover.css | 16 +- .../controlsfx/skin/PopOverSkin.java | 244 +++++---- 3 files changed, 505 insertions(+), 228 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/components/controlsfx/control/PopOver.java b/desktop/src/main/java/bisq/desktop/components/controlsfx/control/PopOver.java index 9593cbc4302..0b2c8790bde 100644 --- a/desktop/src/main/java/bisq/desktop/components/controlsfx/control/PopOver.java +++ b/desktop/src/main/java/bisq/desktop/components/controlsfx/control/PopOver.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2013, ControlsFX +/* + * Copyright (c) 2013, 2016 ControlsFX * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,14 +24,26 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package bisq.desktop.components.controlsfx.control; -import static java.util.Objects.requireNonNull; -import static javafx.scene.input.MouseEvent.MOUSE_CLICKED; import bisq.desktop.components.controlsfx.skin.PopOverSkin; + import javafx.animation.FadeTransition; + +import javafx.stage.Window; +import javafx.stage.WindowEvent; + +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.PopupControl; +import javafx.scene.control.Skin; +import javafx.scene.layout.StackPane; + +import javafx.geometry.Bounds; +import javafx.geometry.Insets; + import javafx.beans.InvalidationListener; -import javafx.beans.Observable; import javafx.beans.WeakInvalidationListener; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; @@ -42,37 +54,47 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import javafx.beans.value.WeakChangeListener; + import javafx.event.EventHandler; -import javafx.geometry.Bounds; -import javafx.geometry.Insets; -import javafx.scene.Node; -import javafx.scene.control.Label; -import javafx.scene.control.PopupControl; -import javafx.scene.control.Skin; -import javafx.scene.input.MouseEvent; -import javafx.stage.Window; -import javafx.stage.WindowEvent; +import javafx.event.WeakEventHandler; + import javafx.util.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static java.util.Objects.requireNonNull; +import static javafx.scene.input.MouseEvent.MOUSE_CLICKED; + /** * The PopOver control provides detailed information about an owning node in a * popup window. The popup window has a very lightweight appearance (no default * window decorations) and an arrow pointing at the owner. Due to the nature of * popup windows the PopOver will move around with the parent window when the * user drags it.
- *

+ *
Screenshot of PopOver

* The PopOver can be detached from the owning node by dragging it away from the * owner. It stops displaying an arrow and starts displaying a title and a close * icon.
*
- *

+ *
Screenshot of a detached PopOver

* The following image shows a popover with an accordion content node. PopOver * controls are automatically resizing themselves when the content node changes * its size.
*
- *

+ *
Screenshot of PopOver containing an Accordion

+ * For styling apply stylesheets to the root pane of the PopOver. + * + *

Example:

+ * + *
+ * PopOver popOver = new PopOver();
+ * popOver.getRoot().getStylesheets().add(...);
+ * 
+ * */ public class PopOver extends PopupControl { @@ -84,6 +106,12 @@ public class PopOver extends PopupControl { private double targetY; + private final SimpleBooleanProperty animated = new SimpleBooleanProperty(true); + private final ObjectProperty fadeInDuration = new SimpleObjectProperty<>(DEFAULT_FADE_DURATION); + private final ObjectProperty fadeOutDuration = new SimpleObjectProperty<>(DEFAULT_FADE_DURATION); + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + /** * Creates a pop over with a label as the content node. */ @@ -92,30 +120,24 @@ public PopOver() { getStyleClass().add(DEFAULT_STYLE_CLASS); + getRoot().getStylesheets().add( + requireNonNull(PopOver.class.getResource("popover.css")).toExternalForm()); //$NON-NLS-1$ + setAnchorLocation(AnchorLocation.WINDOW_TOP_LEFT); - setOnHiding(new EventHandler() { - @Override - public void handle(WindowEvent evt) { - setDetached(false); - } - }); + setOnHiding(evt -> setDetached(false)); /* * Create some initial content. */ - Label label = new Label(""); //$NON-NLS-1$ + Label label = new Label("No content set"); //$NON-NLS-1$ label.setPrefSize(200, 200); label.setPadding(new Insets(4)); setContentNode(label); - ChangeListener repositionListener = new ChangeListener() { - @Override - public void changed(ObservableValue value, - Object oldObject, Object newObject) { - if (isShowing() && !isDetached()) { - show(getOwnerNode(), targetX, targetY); - adjustWindowLocation(); - } + InvalidationListener repositionListener = observable -> { + if (isShowing() && !isDetached()) { + show(getOwnerNode(), targetX, targetY); + adjustWindowLocation(); } }; @@ -123,12 +145,21 @@ public void changed(ObservableValue value, cornerRadius.addListener(repositionListener); arrowLocation.addListener(repositionListener); arrowIndent.addListener(repositionListener); + headerAlwaysVisible.addListener(repositionListener); + + /* + * A detached popover should of course not automatically hide itself. + */ + detached.addListener(it -> setAutoHide(!isDetached())); + + setAutoHide(true); } /** * Creates a pop over with the given node as the content node. * - * @param content The content shown by the pop over + * @param content + * The content shown by the pop over */ public PopOver(Node content) { this(); @@ -141,9 +172,28 @@ protected Skin createDefaultSkin() { return new PopOverSkin(this); } + private final StackPane root = new StackPane(); + + /** + * The root pane stores the content node of the popover. It is accessible + * via this method in order to support proper styling. + * + *

Example:

+ * + *
+     * PopOver popOver = new PopOver();
+     * popOver.getRoot().getStylesheets().add(...);
+     * 
+ * + * @return the root pane + */ + public final StackPane getRoot() { + return root; + } + // Content support. - private final ObjectProperty contentNode = new SimpleObjectProperty( + private final ObjectProperty contentNode = new SimpleObjectProperty<>( this, "contentNode") { //$NON-NLS-1$ @Override public void setValue(Node node) { @@ -151,7 +201,8 @@ public void setValue(Node node) { throw new IllegalArgumentException( "content node can not be null"); //$NON-NLS-1$ } - }; + } + }; /** @@ -186,41 +237,36 @@ public final void setContentNode(Node content) { contentNodeProperty().set(content); } - private InvalidationListener hideListener = new InvalidationListener() { - @Override - public void invalidated(Observable observable) { - if (!isDetached()) { - hide(Duration.ZERO); - } + private final InvalidationListener hideListener = observable -> { + if (!isDetached()) { + hide(Duration.ZERO); } }; - private WeakInvalidationListener weakHideListener = new WeakInvalidationListener( + private final WeakInvalidationListener weakHideListener = new WeakInvalidationListener( hideListener); - private ChangeListener xListener = new ChangeListener() { - @Override - public void changed(ObservableValue value, - Number oldX, Number newX) { - setX(getX() + (newX.doubleValue() - oldX.doubleValue())); + private final ChangeListener xListener = (value, oldX, newX) -> { + if (!isDetached()) { + setAnchorX(getAnchorX() + (newX.doubleValue() - oldX.doubleValue())); } }; - private WeakChangeListener weakXListener = new WeakChangeListener<>( + private final WeakChangeListener weakXListener = new WeakChangeListener<>( xListener); - private ChangeListener yListener = new ChangeListener() { - @Override - public void changed(ObservableValue value, - Number oldY, Number newY) { - setY(getY() + (newY.doubleValue() - oldY.doubleValue())); + private final ChangeListener yListener = (value, oldY, newY) -> { + if (!isDetached()) { + setAnchorY(getAnchorY() + (newY.doubleValue() - oldY.doubleValue())); } }; - private WeakChangeListener weakYListener = new WeakChangeListener<>( + private final WeakChangeListener weakYListener = new WeakChangeListener<>( yListener); private Window ownerWindow; + private final EventHandler closePopOverOnOwnerWindowCloseLambda = event -> ownerWindowClosing(); + private final WeakEventHandler closePopOverOnOwnerWindowClose = new WeakEventHandler<>(closePopOverOnOwnerWindowCloseLambda); /** * Shows the pop over in a position relative to the edges of the given owner @@ -286,6 +332,38 @@ public final void show(Node owner, double offset) { } } + /** {@inheritDoc} */ + @Override + public final void show(Window owner) { + super.show(owner); + ownerWindow = owner; + + if (isAnimated()) { + showFadeInAnimation(getFadeInDuration()); + } + + ownerWindow.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, + closePopOverOnOwnerWindowClose); + ownerWindow.addEventFilter(WindowEvent.WINDOW_HIDING, + closePopOverOnOwnerWindowClose); + } + + /** {@inheritDoc} */ + @Override + public final void show(Window ownerWindow, double anchorX, double anchorY) { + super.show(ownerWindow, anchorX, anchorY); + this.ownerWindow = ownerWindow; + + if (isAnimated()) { + showFadeInAnimation(getFadeInDuration()); + } + + ownerWindow.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, + closePopOverOnOwnerWindowClose); + ownerWindow.addEventFilter(WindowEvent.WINDOW_HIDING, + closePopOverOnOwnerWindowClose); + } + /** * Makes the pop over visible at the give location and associates it with * the given owner node. The x and y coordinate will be the target location @@ -300,7 +378,7 @@ public final void show(Node owner, double offset) { */ @Override public final void show(Node owner, double x, double y) { - show(owner, x, y, DEFAULT_FADE_DURATION); + show(owner, x, y, getFadeInDuration()); } /** @@ -315,14 +393,14 @@ public final void show(Node owner, double x, double y) { * @param y * the y coordinate for the pop over arrow tip * @param fadeInDuration - * the time it takes for the pop over to be fully visible + * the time it takes for the pop over to be fully visible. This duration takes precedence over the fade-in property without setting. */ public final void show(Node owner, double x, double y, Duration fadeInDuration) { /* - * Calling show() a second time without first closing the - * pop over causes it to be placed at the wrong location. + * Calling show() a second time without first closing the pop over + * causes it to be placed at the wrong location. */ if (ownerWindow != null && isShowing()) { super.hide(); @@ -360,18 +438,15 @@ public final void show(Node owner, double x, double y, /* * The user clicked somewhere into the transparent background. If - * this is the case the hide the window (when attached). + * this is the case then hide the window (when attached). */ - getScene().addEventHandler(MOUSE_CLICKED, - new EventHandler() { - public void handle(MouseEvent evt) { - if (evt.getTarget().equals(getScene().getRoot())) { - if (!isDetached()) { - hide(); - } - } - }; - }); + getScene().addEventHandler(MOUSE_CLICKED, mouseEvent -> { + if (mouseEvent.getTarget().equals(getScene().getRoot())) { + if (!isDetached()) { + hide(); + } + } + }); /* * Move the window so that the arrow will end up pointing at the @@ -382,6 +457,18 @@ public void handle(MouseEvent evt) { super.show(owner, x, y); + if (isAnimated()) { + showFadeInAnimation(fadeInDuration); + } + + // Bug fix - close popup when owner window is closing + ownerWindow.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, + closePopOverOnOwnerWindowClose); + ownerWindow.addEventFilter(WindowEvent.WINDOW_HIDING, + closePopOverOnOwnerWindowClose); + } + + private void showFadeInAnimation(Duration fadeInDuration) { // Fade In Node skinNode = getSkin().getNode(); skinNode.setOpacity(0); @@ -392,6 +479,10 @@ public void handle(MouseEvent evt) { fadeIn.play(); } + private void ownerWindowClosing() { + hide(Duration.ZERO); + } + /** * Hides the pop over by quickly changing its opacity to 0. * @@ -399,7 +490,7 @@ public void handle(MouseEvent evt) { */ @Override public final void hide() { - hide(DEFAULT_FADE_DURATION); + hide(getFadeOutDuration()); } /** @@ -411,21 +502,32 @@ public final void hide() { * @since 1.0 */ public final void hide(Duration fadeOutDuration) { + log.info("hide:" + fadeOutDuration.toString()); + //We must remove EventFilter in order to prevent memory leak. + if (ownerWindow != null) { + ownerWindow.removeEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, + closePopOverOnOwnerWindowClose); + ownerWindow.removeEventFilter(WindowEvent.WINDOW_HIDING, + closePopOverOnOwnerWindowClose); + } if (fadeOutDuration == null) { fadeOutDuration = DEFAULT_FADE_DURATION; } if (isShowing()) { - // Fade Out - Node skinNode = getSkin().getNode(); - skinNode.setOpacity(0); - - FadeTransition fadeOut = new FadeTransition(fadeOutDuration, - skinNode); - fadeOut.setFromValue(1); - fadeOut.setToValue(0); - fadeOut.setOnFinished(evt -> super.hide()); - fadeOut.play(); + if (isAnimated()) { + // Fade Out + Node skinNode = getSkin().getNode(); + + FadeTransition fadeOut = new FadeTransition(fadeOutDuration, + skinNode); + fadeOut.setFromValue(skinNode.getOpacity()); + fadeOut.setToValue(0); + fadeOut.setOnFinished(evt -> super.hide()); + fadeOut.play(); + } else { + super.hide(); + } } } @@ -436,26 +538,26 @@ private void adjustWindowLocation() { case TOP_CENTER: case TOP_LEFT: case TOP_RIGHT: - setX(getX() + bounds.getMinX() - computeXOffset()); - setY(getY() + bounds.getMinY() + getArrowSize()); + setAnchorX(getAnchorX() + bounds.getMinX() - computeXOffset()); + setAnchorY(getAnchorY() + bounds.getMinY() + getArrowSize()); break; case LEFT_TOP: case LEFT_CENTER: case LEFT_BOTTOM: - setX(getX() + bounds.getMinX() + getArrowSize()); - setY(getY() + bounds.getMinY() - computeYOffset()); + setAnchorX(getAnchorX() + bounds.getMinX() + getArrowSize()); + setAnchorY(getAnchorY() + bounds.getMinY() - computeYOffset()); break; case BOTTOM_CENTER: case BOTTOM_LEFT: case BOTTOM_RIGHT: - setX(getX() + bounds.getMinX() - computeXOffset()); - setY(getY() - bounds.getMinY() - bounds.getMaxY() - 1); + setAnchorX(getAnchorX() + bounds.getMinX() - computeXOffset()); + setAnchorY(getAnchorY() - bounds.getMinY() - bounds.getMaxY() - 1); break; case RIGHT_TOP: case RIGHT_BOTTOM: case RIGHT_CENTER: - setX(getX() - bounds.getMinX() - bounds.getMaxX() - 1); - setY(getY() + bounds.getMinY() - computeYOffset()); + setAnchorX(getAnchorX() - bounds.getMinX() - bounds.getMaxX() - 1); + setAnchorY(getAnchorY() + bounds.getMinY() - computeYOffset()); break; } } @@ -508,6 +610,74 @@ public final void detach() { } } + // always show header + + private final BooleanProperty headerAlwaysVisible = new SimpleBooleanProperty(this, "headerAlwaysVisible"); //$NON-NLS-1$ + + /** + * Determines whether or not the {@link PopOver} header should remain visible, even while attached. + */ + public final BooleanProperty headerAlwaysVisibleProperty() { + return headerAlwaysVisible; + } + + /** + * Sets the value of the headerAlwaysVisible property. + * + * @param visible + * if true, then the header is visible even while attached + * + * @see #headerAlwaysVisibleProperty() + */ + public final void setHeaderAlwaysVisible(boolean visible) { + headerAlwaysVisible.setValue(visible); + } + + /** + * Returns the value of the detachable property. + * + * @return true if the header is visible even while attached + * + * @see #headerAlwaysVisibleProperty() + */ + public final boolean isHeaderAlwaysVisible() { + return headerAlwaysVisible.getValue(); + } + + // enable close button + + private final BooleanProperty closeButtonEnabled = new SimpleBooleanProperty(this, "closeButtonEnabled", true); //$NON-NLS-1$ + + /** + * Determines whether or not the header's close button should be available. + */ + public final BooleanProperty closeButtonEnabledProperty() { + return closeButtonEnabled; + } + + /** + * Sets the value of the closeButtonEnabled property. + * + * @param enabled + * if false, the pop over will not be closeable by the header's close button + * + * @see #closeButtonEnabledProperty() + */ + public final void setCloseButtonEnabled(boolean enabled) { + closeButtonEnabled.setValue(enabled); + } + + /** + * Returns the value of the closeButtonEnabled property. + * + * @return true if the header's close button is enabled + * + * @see #closeButtonEnabledProperty() + */ + public final boolean isCloseButtonEnabled() { + return closeButtonEnabled.getValue(); + } + // detach support private final BooleanProperty detachable = new SimpleBooleanProperty(this, @@ -561,7 +731,7 @@ public final BooleanProperty detachedProperty() { * Sets the value of the detached property. * * @param detached - * if true the pop over will change its appearance to "detached" + * if true the pop over will change its apperance to "detached" * mode * * @see #detachedProperty() @@ -701,46 +871,42 @@ public final void setCornerRadius(double radius) { // Detached stage title - private final StringProperty detachedTitle = new SimpleStringProperty(this, - "detachedTitle", "Info"); //$NON-NLS-1$ //$NON-NLS-2$ + private final StringProperty title = new SimpleStringProperty(this, "title", "No title set"); //$NON-NLS-1$ //$NON-NLS-2$ /** - * Stores the title to display when the pop over becomes detached. + * Stores the title to display in the PopOver's header. * - * @return the detached title property + * @return the title property */ - public final StringProperty detachedTitleProperty() { - return detachedTitle; + public final StringProperty titleProperty() { + return title; } /** - * Returns the value of the detached title property. + * Returns the value of the title property. * * @return the detached title - * - * @see #detachedTitleProperty() + * @see #titleProperty() */ - public final String getDetachedTitle() { - return detachedTitleProperty().get(); + public final String getTitle() { + return titleProperty().get(); } /** - * Sets the value of the detached title property. + * Sets the value of the title property. * - * @param title - * the title to use when detached - * - * @see #detachedTitleProperty() + * @param title the title to use when detached + * @see #titleProperty() */ - public final void setDetachedTitle(String title) { + public final void setTitle(String title) { if (title == null) { throw new IllegalArgumentException("title can not be null"); //$NON-NLS-1$ } - detachedTitleProperty().set(title); + titleProperty().set(title); } - private final ObjectProperty arrowLocation = new SimpleObjectProperty( + private final ObjectProperty arrowLocation = new SimpleObjectProperty<>( this, "arrowLocation", ArrowLocation.LEFT_TOP); //$NON-NLS-1$ /** @@ -782,6 +948,93 @@ public final ArrowLocation getArrowLocation() { * All possible arrow locations. */ public enum ArrowLocation { - LEFT_TOP, LEFT_CENTER, LEFT_BOTTOM, RIGHT_TOP, RIGHT_CENTER, RIGHT_BOTTOM, TOP_LEFT, TOP_CENTER, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT; + LEFT_TOP, LEFT_CENTER, LEFT_BOTTOM, RIGHT_TOP, RIGHT_CENTER, RIGHT_BOTTOM, TOP_LEFT, TOP_CENTER, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT + } + + /** + * Stores the fade-in duration. This should be set before calling PopOver.show(..). + * + * @return the fade-in duration property + */ + public final ObjectProperty fadeInDurationProperty() { + return fadeInDuration; + } + + /** + * Stores the fade-out duration. + * + * @return the fade-out duration property + */ + public final ObjectProperty fadeOutDurationProperty() { + return fadeOutDuration; + } + + /** + * Returns the value of the fade-in duration property. + * + * @return the fade-in duration + * @see #fadeInDurationProperty() + */ + public final Duration getFadeInDuration() { + return fadeInDurationProperty().get(); + } + + /** + * Sets the value of the fade-in duration property. This should be set before calling PopOver.show(..). + * + * @param duration the requested fade-in duration + * @see #fadeInDurationProperty() + */ + public final void setFadeInDuration(Duration duration) { + fadeInDurationProperty().setValue(duration); + } + + /** + * Returns the value of the fade-out duration property. + * + * @return the fade-out duration + * @see #fadeOutDurationProperty() + */ + public final Duration getFadeOutDuration() { + return fadeOutDurationProperty().get(); + } + + /** + * Sets the value of the fade-out duration property. + * + * @param duration the requested fade-out duration + * @see #fadeOutDurationProperty() + */ + public final void setFadeOutDuration(Duration duration) { + fadeOutDurationProperty().setValue(duration); + } + + /** + * Stores the "animated" flag. If true then the PopOver will be shown / hidden with a short fade in / out animation. + * + * @return the "animated" property + */ + public final BooleanProperty animatedProperty() { + return animated; + } + + /** + * Returns the value of the "animated" property. + * + * @return true if the PopOver will be shown and hidden with a short fade animation + * @see #animatedProperty() + */ + public final boolean isAnimated() { + return animatedProperty().get(); + } + + /** + * Sets the value of the "animated" property. + * + * @param animated if true the PopOver will be shown and hidden with a short fade animation + * @see #animatedProperty() + */ + public final void setAnimated(boolean animated) { + animatedProperty().set(animated); } } diff --git a/desktop/src/main/java/bisq/desktop/components/controlsfx/control/popover.css b/desktop/src/main/java/bisq/desktop/components/controlsfx/control/popover.css index ccc6d5e42f9..79f946fe6e3 100644 --- a/desktop/src/main/java/bisq/desktop/components/controlsfx/control/popover.css +++ b/desktop/src/main/java/bisq/desktop/components/controlsfx/control/popover.css @@ -3,10 +3,10 @@ } .popover > .border { - -fx-stroke: linear-gradient(to bottom, rgba(0,0,0, .3), rgba(0, 0, 0, .7)) ; - -fx-stroke-width: 0.5; - -fx-fill: rgba(255.0,255.0,255.0, .95); - -fx-effect: dropshadow(gaussian, rgba(0,0,0,.2), 10.0, 0.5, 2.0, 2.0); + -fx-stroke: linear-gradient(to bottom, rgba(0, 0, 0, .3), rgba(0, 0, 0, .7)); + -fx-stroke-width: 1; + -fx-fill: rgba(255.0, 255.0, 255.0, .95); + -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, .2), 10.0, 0.5, 2.0, 2.0); } .popover > .content { @@ -15,7 +15,7 @@ .popover > .detached { } -.popover > .content > .title > .text { +.popover > .content > .title > .text { -fx-padding: 6.0 6.0 0.0 6.0; -fx-text-fill: rgba(120, 120, 120, .8); -fx-font-weight: bold; @@ -26,11 +26,11 @@ } .popover > .content > .title > .icon > .graphics > .circle { - -fx-fill: gray ; - -fx-effect: innershadow(gaussian, rgba(0,0,0,.2), 3, 0.5, 1.0, 1.0); + -fx-fill: gray; + -fx-effect: innershadow(gaussian, rgba(0, 0, 0, .2), 3, 0.5, 1.0, 1.0); } .popover > .content > .title > .icon > .graphics > .line { - -fx-stroke: white ; + -fx-stroke: white; -fx-stroke-width: 2; } diff --git a/desktop/src/main/java/bisq/desktop/components/controlsfx/skin/PopOverSkin.java b/desktop/src/main/java/bisq/desktop/components/controlsfx/skin/PopOverSkin.java index 3ebdc843bf2..e449f847a1a 100644 --- a/desktop/src/main/java/bisq/desktop/components/controlsfx/skin/PopOverSkin.java +++ b/desktop/src/main/java/bisq/desktop/components/controlsfx/skin/PopOverSkin.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2013 - 2015, ControlsFX * All rights reserved. * @@ -26,24 +26,11 @@ */ package bisq.desktop.components.controlsfx.skin; -import static java.lang.Double.MAX_VALUE; -import static javafx.geometry.Pos.CENTER_LEFT; -import static javafx.scene.control.ContentDisplay.GRAPHIC_ONLY; -import static bisq.desktop.components.controlsfx.control.PopOver.ArrowLocation.*; +import bisq.desktop.components.controlsfx.control.PopOver; +import bisq.desktop.components.controlsfx.control.PopOver.ArrowLocation; -import java.util.ArrayList; -import java.util.List; +import javafx.stage.Window; -import javafx.beans.InvalidationListener; -import javafx.beans.Observable; -import javafx.beans.binding.Bindings; -import javafx.beans.property.DoubleProperty; -import javafx.beans.property.SimpleDoubleProperty; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.event.EventHandler; -import javafx.geometry.Point2D; -import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.control.Label; @@ -60,10 +47,28 @@ import javafx.scene.shape.PathElement; import javafx.scene.shape.QuadCurveTo; import javafx.scene.shape.VLineTo; -import javafx.stage.Window; -import bisq.desktop.components.controlsfx.control.PopOver; -import bisq.desktop.components.controlsfx.control.PopOver.ArrowLocation; +import javafx.geometry.Point2D; +import javafx.geometry.Pos; + +import javafx.beans.InvalidationListener; +import javafx.beans.binding.Bindings; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.SimpleDoubleProperty; + +import javafx.event.EventHandler; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static bisq.desktop.components.controlsfx.control.PopOver.ArrowLocation.*; +import static java.lang.Double.MAX_VALUE; +import static javafx.geometry.Pos.CENTER_LEFT; +import static javafx.scene.control.ContentDisplay.GRAPHIC_ONLY; +import static javafx.scene.paint.Color.YELLOW; public class PopOverSkin implements Skin { @@ -74,27 +79,27 @@ public class PopOverSkin implements Skin { private boolean tornOff; - private Label title; - private Label closeIcon; + private final Path path; + private final Path clip; - private Path path; - private BorderPane content; - private StackPane titlePane; - private StackPane stackPane; + private final BorderPane content; + private final StackPane titlePane; + private final StackPane stackPane; private Point2D dragStartLocation; - private PopOver popOver; + private final PopOver popOver; + + private final Logger log = LoggerFactory.getLogger(this.getClass()); public PopOverSkin(final PopOver popOver) { this.popOver = popOver; - stackPane = new StackPane(); - stackPane.getStylesheets().add( - PopOver.class.getResource("popover.css").toExternalForm()); //$NON-NLS-1$ + stackPane = popOver.getRoot(); stackPane.setPickOnBounds(false); - stackPane.getStyleClass().add("popover"); //$NON-NLS-1$ + + Bindings.bindContent(stackPane.getStyleClass(), popOver.getStyleClass()); /* * The min width and height equal 2 * corner radius + 2 * arrow indent + @@ -110,26 +115,22 @@ public PopOverSkin(final PopOver popOver) { stackPane.minHeightProperty().bind(stackPane.minWidthProperty()); - title = new Label(); - title.textProperty().bind(popOver.detachedTitleProperty()); + Label title = new Label(); + title.textProperty().bind(popOver.titleProperty()); title.setMaxSize(MAX_VALUE, MAX_VALUE); title.setAlignment(Pos.CENTER); title.getStyleClass().add("text"); //$NON-NLS-1$ - closeIcon = new Label(); + Label closeIcon = new Label(); closeIcon.setGraphic(createCloseIcon()); closeIcon.setMaxSize(MAX_VALUE, MAX_VALUE); closeIcon.setContentDisplay(GRAPHIC_ONLY); - closeIcon.visibleProperty().bind(popOver.detachedProperty()); + closeIcon.visibleProperty().bind( + popOver.closeButtonEnabledProperty().and( + popOver.detachedProperty().or(popOver.headerAlwaysVisibleProperty()))); closeIcon.getStyleClass().add("icon"); //$NON-NLS-1$ closeIcon.setAlignment(CENTER_LEFT); - closeIcon.getGraphic().setOnMouseClicked( - new EventHandler() { - @Override - public void handle(MouseEvent evt) { - popOver.hide(); - } - }); + closeIcon.getGraphic().setOnMouseClicked(evt -> popOver.hide()); titlePane = new StackPane(); titlePane.getChildren().add(title); @@ -140,105 +141,125 @@ public void handle(MouseEvent evt) { content.setCenter(popOver.getContentNode()); content.getStyleClass().add("content"); //$NON-NLS-1$ - if (popOver.isDetached()) { + if (popOver.isDetached() || popOver.isHeaderAlwaysVisible()) { content.setTop(titlePane); + } + + if (popOver.isDetached()) { popOver.getStyleClass().add(DETACHED_STYLE_CLASS); content.getStyleClass().add(DETACHED_STYLE_CLASS); } - InvalidationListener updatePathListener = new InvalidationListener() { - - @Override - public void invalidated(Observable observable) { - updatePath(); + popOver.headerAlwaysVisibleProperty().addListener((o, oV, isVisible) -> { + if (isVisible) { + content.setTop(titlePane); + } else if (!popOver.isDetached()) { + content.setTop(null); } - }; + }); + InvalidationListener updatePathListener = observable -> updatePath(); getPopupWindow().xProperty().addListener(updatePathListener); getPopupWindow().yProperty().addListener(updatePathListener); - popOver.arrowLocationProperty().addListener(updatePathListener); + popOver.contentNodeProperty().addListener( + (value, oldContent, newContent) -> content + .setCenter(newContent)); + popOver.detachedProperty() + .addListener((value, oldDetached, newDetached) -> { + + if (newDetached) { + popOver.getStyleClass().add(DETACHED_STYLE_CLASS); + content.getStyleClass().add(DETACHED_STYLE_CLASS); + content.setTop(titlePane); + + switch (getSkinnable().getArrowLocation()) { + case LEFT_TOP: + case LEFT_CENTER: + case LEFT_BOTTOM: + popOver.setAnchorX( + popOver.getAnchorX() + popOver.getArrowSize()); + break; + case TOP_LEFT: + case TOP_CENTER: + case TOP_RIGHT: + popOver.setAnchorY( + popOver.getAnchorY() + popOver.getArrowSize()); + break; + default: + break; + } + } else { + popOver.getStyleClass().remove(DETACHED_STYLE_CLASS); + content.getStyleClass().remove(DETACHED_STYLE_CLASS); + + if (!popOver.isHeaderAlwaysVisible()) { + content.setTop(null); + } + } - popOver.contentNodeProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue value, - Node oldContent, Node newContent) { - content.setCenter(newContent); - } - }); + popOver.sizeToScene(); - popOver.detachedProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue value, - Boolean oldDetached, Boolean newDetached) { - - updatePath(); - - if (newDetached) { - popOver.getStyleClass().add(DETACHED_STYLE_CLASS); - content.getStyleClass().add(DETACHED_STYLE_CLASS); - content.setTop(titlePane); - } else { - popOver.getStyleClass().remove(DETACHED_STYLE_CLASS); - content.getStyleClass().remove(DETACHED_STYLE_CLASS); - content.setTop(null); - } - } - }); + updatePath(); + }); path = new Path(); path.getStyleClass().add("border"); //$NON-NLS-1$ path.setManaged(false); + clip = new Path(); + + /* + * The clip is a path and the path has to be filled with a color. + * Otherwise clipping will not work. + */ + clip.setFill(YELLOW); + createPathElements(); updatePath(); - final EventHandler mousePressedHandler = new EventHandler() { - public void handle(MouseEvent evt) { - if (popOver.isDetachable() || popOver.isDetached()) { - tornOff = false; + final EventHandler mousePressedHandler = evt -> { + log.info("mousePressed:" + popOver.isDetachable() + "," + popOver.isDetached()); + if (popOver.isDetachable() || popOver.isDetached()) { + tornOff = false; - xOffset = evt.getScreenX(); - yOffset = evt.getScreenY(); + xOffset = evt.getScreenX(); + yOffset = evt.getScreenY(); - dragStartLocation = new Point2D(xOffset, yOffset); - } - }; + dragStartLocation = new Point2D(xOffset, yOffset); + } }; - final EventHandler mouseReleasedHandler = new EventHandler() { - public void handle(MouseEvent evt) { - if (tornOff && !getSkinnable().isDetached()) { - tornOff = false; - getSkinnable().detach(); - } - }; + final EventHandler mouseReleasedHandler = evt -> { + log.info("mouseReleased:tornOff" + tornOff + ", " + !getSkinnable().isDetached()); + if (tornOff && !getSkinnable().isDetached()) { + tornOff = false; + getSkinnable().detach(); + } }; - final EventHandler mouseDragHandler = new EventHandler() { - - public void handle(MouseEvent evt) { - if (popOver.isDetachable() || popOver.isDetached()) { - double deltaX = evt.getScreenX() - xOffset; - double deltaY = evt.getScreenY() - yOffset; + final EventHandler mouseDragHandler = evt -> { + log.info("mouseDrag:" + popOver.isDetachable() + "," + popOver.isDetached()); + if (popOver.isDetachable() || popOver.isDetached()) { + double deltaX = evt.getScreenX() - xOffset; + double deltaY = evt.getScreenY() - yOffset; - Window window = getSkinnable().getScene().getWindow(); + Window window = getSkinnable().getScene().getWindow(); - window.setX(window.getX() + deltaX); - window.setY(window.getY() + deltaY); + window.setX(window.getX() + deltaX); + window.setY(window.getY() + deltaY); - xOffset = evt.getScreenX(); - yOffset = evt.getScreenY(); + xOffset = evt.getScreenX(); + yOffset = evt.getScreenY(); - if (dragStartLocation.distance(xOffset, yOffset) > 20) { - tornOff = true; - updatePath(); - } else if (tornOff) { - tornOff = false; - updatePath(); - } + if (dragStartLocation.distance(xOffset, yOffset) > 20) { + tornOff = true; + updatePath(); + } else if (tornOff) { + tornOff = false; + updatePath(); } - }; + } }; stackPane.setOnMousePressed(mousePressedHandler); @@ -247,6 +268,8 @@ public void handle(MouseEvent evt) { stackPane.getChildren().add(path); stackPane.getChildren().add(content); + + content.setClip(clip); } @Override @@ -689,5 +712,6 @@ private void updatePath() { elements.add(topCurveTo); path.getElements().setAll(elements); + clip.getElements().setAll(elements); } } From fdfcd06eab9f0db83163a2f0650583da9f7810cc Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 16 Jul 2021 09:23:45 +0200 Subject: [PATCH 07/11] Prevent to open wrong info popup when it is your own offer --- .../desktop/main/offer/offerbook/OfferBookView.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index 75f4e45ae7b..c6f8b40ebf2 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -1052,11 +1052,13 @@ public void updateItem(final OfferBookListItem item, boolean empty) { tableRow.setOnMousePressed(null); } else { button.setDefaultButton(false); - tableRow.setOnMousePressed(e -> { - // ugly hack to get the icon clickable when deactivated - if (!(e.getTarget() instanceof ImageView || e.getTarget() instanceof Canvas)) - onShowInfo(offer, canTakeOfferResult); - }); + if (!myOffer) { + tableRow.setOnMousePressed(e -> { + // ugly hack to get the icon clickable when deactivated + if (!(e.getTarget() instanceof ImageView || e.getTarget() instanceof Canvas)) + onShowInfo(offer, canTakeOfferResult); + }); + } } } From ee989a41fdba96394a7e280c937bb5390be3d4a9 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 16 Jul 2021 11:35:52 +0200 Subject: [PATCH 08/11] Adapt style to match @pedromvp design as close as possible --- desktop/src/main/java/bisq/desktop/bisq.css | 13 ++- .../components/AccountStatusTooltipLabel.java | 83 ++++++++++++------- .../desktop/components/ExternalHyperlink.java | 4 + .../desktop/components/HyperlinkWithIcon.java | 8 +- 4 files changed, 77 insertions(+), 31 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/bisq.css b/desktop/src/main/java/bisq/desktop/bisq.css index 49844e0c41b..8b0b830f901 100644 --- a/desktop/src/main/java/bisq/desktop/bisq.css +++ b/desktop/src/main/java/bisq/desktop/bisq.css @@ -1921,8 +1921,19 @@ textfield */ -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, -1, 3); } +.account-status-title { + -fx-font-size: 0.769em; + -fx-font-family: "IBM Plex Sans Medium"; +} + .account-status-inactive-info-item { - -fx-text-fill: -bs-color-gray-ddd; + -fx-text-fill: -bs-color-gray-dim; + -fx-fill: -bs-color-gray-dim; +} + +.account-status-active-info-item { + -fx-text-fill: -fx-accent; + -fx-fill: -fx-accent; } #price-feed-combo { diff --git a/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java index 33559b77325..03cb3f8c9f4 100644 --- a/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java +++ b/desktop/src/main/java/bisq/desktop/components/AccountStatusTooltipLabel.java @@ -27,25 +27,32 @@ import bisq.core.offer.OfferRestrictions; import bisq.core.util.coin.CoinFormatter; -import de.jensd.fx.fontawesome.AwesomeIcon; +import bisq.common.UserThread; + +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; import javafx.scene.Node; import javafx.scene.control.ContentDisplay; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.geometry.Bounds; import javafx.geometry.Insets; import javafx.geometry.Pos; +import java.util.concurrent.TimeUnit; + public class AccountStatusTooltipLabel extends AutoTooltipLabel { public static final int DEFAULT_WIDTH = 300; private final Node textIcon; - private final PopOverWrapper popoverWrapper = new PopOverWrapper(); private final OfferBookListItem.WitnessAgeData witnessAgeData; private final String popupTitle; + private PopOver popOver; + private boolean keepPopOverVisible = false; public AccountStatusTooltipLabel(OfferBookListItem.WitnessAgeData witnessAgeData, CoinFormatter formatter) { @@ -62,8 +69,15 @@ public AccountStatusTooltipLabel(OfferBookListItem.WitnessAgeData witnessAgeData private void positionAndActivateIcon() { textIcon.setOpacity(0.4); textIcon.getStyleClass().add("tooltip-icon"); - textIcon.setOnMouseEntered(e -> popoverWrapper.showPopOver(this::createPopOver)); - textIcon.setOnMouseExited(e -> popoverWrapper.hidePopOver()); + popOver = createPopOver(); + textIcon.setOnMouseEntered(e -> showPopup(textIcon)); + + textIcon.setOnMouseExited(e -> UserThread.runAfter(() -> { + if (!keepPopOverVisible) { + popOver.hide(); + } + }, 200, TimeUnit.MILLISECONDS) + ); setGraphic(textIcon); setContentDisplay(ContentDisplay.RIGHT); @@ -73,15 +87,14 @@ private PopOver createPopOver() { Label titleLabel = new Label(popupTitle); titleLabel.setMaxWidth(DEFAULT_WIDTH); titleLabel.setWrapText(true); - titleLabel.setPadding(new Insets(10, 10, 2, 10)); - titleLabel.getStyleClass().add("bold-text"); - titleLabel.getStyleClass().add("default-text"); + titleLabel.setPadding(new Insets(10, 10, 0, 10)); + titleLabel.getStyleClass().add("account-status-title"); Label infoLabel = new Label(witnessAgeData.getInfo()); infoLabel.setMaxWidth(DEFAULT_WIDTH); infoLabel.setWrapText(true); - infoLabel.setPadding(new Insets(2, 10, 2, 10)); - infoLabel.getStyleClass().add("default-text"); + infoLabel.setPadding(new Insets(0, 10, 4, 10)); + infoLabel.getStyleClass().add("small-text"); Label buyLabel = createDetailsItem( Res.get("offerbook.timeSinceSigning.tooltip.checkmark.buyBtc"), @@ -92,46 +105,60 @@ private PopOver createPopOver() { witnessAgeData.isLimitLifted() ); - Hyperlink learnMoreLink = new Hyperlink(Res.get("offerbook.timeSinceSigning.tooltip.learnMore")); + Hyperlink learnMoreLink = new ExternalHyperlink(Res.get("offerbook.timeSinceSigning.tooltip.learnMore"), + null, + "0.769em"); learnMoreLink.setMaxWidth(DEFAULT_WIDTH); learnMoreLink.setWrapText(true); learnMoreLink.setPadding(new Insets(10, 10, 2, 10)); - learnMoreLink.getStyleClass().add("very-small-text"); + learnMoreLink.getStyleClass().addAll("very-small-text"); learnMoreLink.setOnAction((e) -> GUIUtil.openWebPage("https://bisq.wiki/Account_limits")); VBox vBox = new VBox(2, titleLabel, infoLabel, buyLabel, waitLabel, learnMoreLink); vBox.setPadding(new Insets(2, 0, 2, 0)); vBox.setAlignment(Pos.CENTER_LEFT); + PopOver popOver = new PopOver(vBox); - if (textIcon.getScene() != null) { - popOver.setDetachable(false); - popOver.setArrowLocation(PopOver.ArrowLocation.LEFT_CENTER); - popOver.show(textIcon, -10); - } + popOver.setArrowLocation(PopOver.ArrowLocation.LEFT_CENTER); + + vBox.setOnMouseEntered(mouseEvent -> keepPopOverVisible = true); + + vBox.setOnMouseExited(mouseEvent -> { + keepPopOverVisible = false; + popOver.hide(); + }); + return popOver; } + private void showPopup(Node textIcon) { + Bounds bounds = textIcon.localToScreen(textIcon.getBoundsInLocal()); + popOver.show(textIcon, bounds.getMaxX() + 10, (bounds.getMinY() + bounds.getHeight() / 2) - 10); + } + private Label createDetailsItem(String text, boolean active) { - Label icon = FormBuilder.getIcon(active ? AwesomeIcon.OK_SIGN : AwesomeIcon.REMOVE_SIGN); - icon.setLayoutY(4); - icon.getStyleClass().add("icon"); + Label label = new Label(text); + label.setMaxWidth(DEFAULT_WIDTH); + label.setWrapText(true); + label.setPadding(new Insets(0, 10, 0, 10)); + label.getStyleClass().add("small-text"); if (active) { - icon.getStyleClass().add("highlight"); + label.setStyle("-fx-text-fill: -fx-accent"); } else { - icon.getStyleClass().add("account-status-inactive-info-item"); + label.setStyle("-fx-text-fill: -bs-color-gray-dim"); } - Label label = new Label(text, icon); - label.setMaxWidth(DEFAULT_WIDTH); - label.setWrapText(true); - label.setPadding(new Insets(0, 10, 0, 10)); - label.getStyleClass().addAll("small-text"); + Text icon = FormBuilder.getSmallIconForLabel(active ? + MaterialDesignIcon.CHECKBOX_MARKED_CIRCLE : MaterialDesignIcon.CLOSE_CIRCLE, label); + icon.setLayoutY(4); + if (active) { - label.getStyleClass().add("success-text"); + icon.getStyleClass().add("account-status-active-info-item"); } else { - label.getStyleClass().add("account-status-inactive-info-item"); + icon.getStyleClass().add("account-status-inactive-info-item"); } + return label; } } diff --git a/desktop/src/main/java/bisq/desktop/components/ExternalHyperlink.java b/desktop/src/main/java/bisq/desktop/components/ExternalHyperlink.java index 62f7cf12106..a6ce7483c74 100644 --- a/desktop/src/main/java/bisq/desktop/components/ExternalHyperlink.java +++ b/desktop/src/main/java/bisq/desktop/components/ExternalHyperlink.java @@ -28,4 +28,8 @@ public ExternalHyperlink(String text) { public ExternalHyperlink(String text, String style) { super(text, MaterialDesignIcon.LINK, style); } + + public ExternalHyperlink(String text, String style, String iconSize) { + super(text, MaterialDesignIcon.LINK, style, iconSize); + } } diff --git a/desktop/src/main/java/bisq/desktop/components/HyperlinkWithIcon.java b/desktop/src/main/java/bisq/desktop/components/HyperlinkWithIcon.java index b4a24825e03..b3738ee74d0 100644 --- a/desktop/src/main/java/bisq/desktop/components/HyperlinkWithIcon.java +++ b/desktop/src/main/java/bisq/desktop/components/HyperlinkWithIcon.java @@ -59,10 +59,10 @@ public HyperlinkWithIcon(String text, GlyphIcons icon) { this(text, icon, null); } - public HyperlinkWithIcon(String text, GlyphIcons icon, String style) { + public HyperlinkWithIcon(String text, GlyphIcons icon, String style, String iconSize) { super(text); - Text textIcon = FormBuilder.getIcon(icon); + Text textIcon = FormBuilder.getIcon(icon, iconSize); textIcon.setOpacity(0.7); textIcon.getStyleClass().addAll("hyperlink", "no-underline"); @@ -76,6 +76,10 @@ public HyperlinkWithIcon(String text, GlyphIcons icon, String style) { setIcon(textIcon); } + public HyperlinkWithIcon(String text, GlyphIcons icon, String style) { + this(text, icon, style, "1.231em"); + } + public void hideIcon() { setGraphic(null); } From 4ef8242719cce6aff9d7b1a9de9a201156917df8 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Tue, 27 Jul 2021 13:43:51 +0200 Subject: [PATCH 09/11] Update expired public key --- desktop/package/29CDFD3B.asc | 132 +++++++++++++++++++++++++---------- 1 file changed, 96 insertions(+), 36 deletions(-) diff --git a/desktop/package/29CDFD3B.asc b/desktop/package/29CDFD3B.asc index 18f37a913f8..96cf1950601 100644 --- a/desktop/package/29CDFD3B.asc +++ b/desktop/package/29CDFD3B.asc @@ -12,40 +12,100 @@ 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 -NYeOG8Xnm4CxuWqUu0FDMv6BhkMCTz2X4xcnbtI= -=9LRS +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 +Bt9fU4wDD1lOCBjI1JG+aQ== +=SPaw -----END PGP PUBLIC KEY BLOCK----- From 8c666dba92388700905779084c5d34d619d42db2 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Sun, 1 Aug 2021 17:22:00 +0200 Subject: [PATCH 10/11] Avoid creating more than one refund payout Double clicking the close ticketbutton creates two payout transactions. This fix makes sure only one payout transaction is created for the dispute. Restarting the client allows for creating another refund transaction for the dispute if needed. --- .../java/bisq/core/support/dispute/Dispute.java | 2 ++ .../main/resources/i18n/displayStrings.properties | 3 +++ .../main/overlays/windows/DisputeSummaryWindow.java | 13 +++++++++++++ 3 files changed, 18 insertions(+) diff --git a/core/src/main/java/bisq/core/support/dispute/Dispute.java b/core/src/main/java/bisq/core/support/dispute/Dispute.java index 98ed154fbee..669f7d08540 100644 --- a/core/src/main/java/bisq/core/support/dispute/Dispute.java +++ b/core/src/main/java/bisq/core/support/dispute/Dispute.java @@ -148,6 +148,8 @@ public static protobuf.Dispute.State toProtoMessage(Dispute.State state) { private transient String uid; @Setter private transient long payoutTxConfirms = -1; + @Setter + private transient boolean payoutDone = false; private transient final BooleanProperty isClosedProperty = new SimpleBooleanProperty(); private transient final IntegerProperty badgeCountProperty = new SimpleIntegerProperty(); diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 0c7c1d863c5..a148833cbe9 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2626,6 +2626,9 @@ disputeSummaryWindow.close.txDetails=Spending: {0}\n\ disputeSummaryWindow.close.noPayout.headline=Close without any payout disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout? +disputeSummaryWindow.close.alreadyPaid.headline=Payout already done +disputeSummaryWindow.close.alreadyPaid.text=Restart the client to do another payout for this dispute + emptyWalletWindow.headline={0} emergency wallet tool emptyWalletWindow.info=Please use that only in emergency case if you cannot access your fund from the UI.\n\n\ Please note that all open offers will be closed automatically when using this tool.\n\n\ diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java index a6ac3ce0ee0..61b46a394ff 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java @@ -673,6 +673,13 @@ private void addButtons(Contract contract) { } private void showPayoutTxConfirmation(Contract contract, DisputeResult disputeResult, ResultHandler resultHandler) { + if (dispute.isPayoutDone()) { + new Popup().headLine(Res.get("disputeSummaryWindow.close.alreadyPaid.headline")) + .confirmation(Res.get("disputeSummaryWindow.close.alreadyPaid.text")) + .closeButtonText(Res.get("shared.cancel")) + .show(); + } + Coin buyerPayoutAmount = disputeResult.getBuyerPayoutAmount(); String buyerPayoutAddressString = contract.getBuyerPayoutAddressString(); Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount(); @@ -734,6 +741,12 @@ private void doPayout(Coin buyerPayoutAmount, String buyerPayoutAddressString, String sellerPayoutAddressString, ResultHandler resultHandler) { + if (dispute.isPayoutDone()) { + log.error("Payout already processed, returning to avoid double payout for dispute of trade {}", + dispute.getTradeId()); + return; + } + dispute.setPayoutDone(true); try { Transaction tx = btcWalletService.createRefundPayoutTx(buyerPayoutAmount, sellerPayoutAmount, From 8d9a03bc390539ca5cae37aaf06a8689adbd49af Mon Sep 17 00:00:00 2001 From: jmacxx <47253594+jmacxx@users.noreply.github.com> Date: Mon, 2 Aug 2021 22:22:29 -0500 Subject: [PATCH 11/11] fix issue with extraData field when editing offer --- .../desktop/main/portfolio/editoffer/EditOfferDataModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java index dc7d29c7c08..889a757b3c4 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java @@ -223,7 +223,7 @@ public void onPublishOffer(ResultHandler resultHandler, ErrorMessageHandler erro offerPayload.getUpperClosePrice(), offerPayload.isPrivateOffer(), offerPayload.getHashOfChallenge(), - offerPayload.getExtraDataMap(), + newOfferPayload.getExtraDataMap(), offerPayload.getProtocolVersion()); final Offer editedOffer = new Offer(editedPayload);