show xmr logo with qr codes

This commit is contained in:
woodser 2025-06-03 06:35:51 -04:00
parent 23d1b8059a
commit 8c9c68482a
No known key found for this signature in database
GPG key ID: 55A10DD48ADEE5EF
5 changed files with 116 additions and 69 deletions

View file

@ -40,6 +40,7 @@ import com.google.inject.name.Named;
import haveno.common.ThreadUtils;
import haveno.common.UserThread;
import haveno.common.app.DevEnv;
import haveno.common.util.Tuple2;
import haveno.common.util.Tuple3;
import haveno.core.locale.Res;
import haveno.core.trade.HavenoUtils;
@ -89,10 +90,9 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import monero.common.MoneroUtils;
import monero.wallet.model.MoneroTxConfig;
import monero.wallet.model.MoneroWalletListener;
import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType;
@ -111,6 +111,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
@FXML
TableColumn<DepositListItem, DepositListItem> addressColumn, balanceColumn, confirmationsColumn, usageColumn;
private ImageView qrCodeImageView;
private StackPane qrCodePane;
private AddressTextField addressTextField;
private Button generateNewAddressButton;
private TitledGroupBg titledGroupBg;
@ -192,19 +193,19 @@ public class DepositView extends ActivatableView<VBox, Void> {
titledGroupBg = addTitledGroupBg(gridPane, gridRow, 4, Res.get("funds.deposit.fundWallet"));
titledGroupBg.getStyleClass().add("last");
qrCodeImageView = new ImageView();
qrCodeImageView.setFitHeight(150);
qrCodeImageView.setFitWidth(150);
qrCodeImageView.getStyleClass().add("qr-code");
Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow")));
qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter(
Tuple2<StackPane, ImageView> qrCodeTuple = GUIUtil.getSmallXmrQrCodePane();
qrCodePane = qrCodeTuple.first;
qrCodeImageView = qrCodeTuple.second;
Tooltip.install(qrCodePane, new Tooltip(Res.get("shared.openLargeQRWindow")));
qrCodePane.setOnMouseClicked(e -> UserThread.runAfter(
() -> new QRCodeWindow(getPaymentUri()).show(),
200, TimeUnit.MILLISECONDS));
GridPane.setRowIndex(qrCodeImageView, gridRow);
GridPane.setRowSpan(qrCodeImageView, 4);
GridPane.setColumnIndex(qrCodeImageView, 1);
GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 10));
gridPane.getChildren().add(qrCodeImageView);
GridPane.setRowIndex(qrCodePane, gridRow);
GridPane.setRowSpan(qrCodePane, 4);
GridPane.setColumnIndex(qrCodePane, 1);
GridPane.setMargin(qrCodePane, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 10));
gridPane.getChildren().add(qrCodePane);
addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.address"), Layout.FIRST_ROW_DISTANCE);
addressTextField.setPaymentLabel(paymentLabelString);
@ -215,8 +216,8 @@ public class DepositView extends ActivatableView<VBox, Void> {
titledGroupBg.setVisible(false);
titledGroupBg.setManaged(false);
qrCodeImageView.setVisible(false);
qrCodeImageView.setManaged(false);
qrCodePane.setVisible(false);
qrCodePane.setManaged(false);
addressTextField.setVisible(false);
addressTextField.setManaged(false);
amountTextField.setManaged(false);
@ -312,8 +313,8 @@ public class DepositView extends ActivatableView<VBox, Void> {
private void fillForm(String address) {
titledGroupBg.setVisible(true);
titledGroupBg.setManaged(true);
qrCodeImageView.setVisible(true);
qrCodeImageView.setManaged(true);
qrCodePane.setVisible(true);
qrCodePane.setManaged(true);
addressTextField.setVisible(true);
addressTextField.setManaged(true);
amountTextField.setManaged(true);

View file

@ -85,6 +85,7 @@ import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.util.StringConverter;
@ -149,6 +150,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
private ComboBox<PaymentAccount> paymentAccountsComboBox;
private ComboBox<TradeCurrency> currencyComboBox;
private ImageView qrCodeImageView;
private StackPane qrCodePane;
private VBox paymentAccountsSelection, currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox;
private HBox fundingHBox, firstRowHBox, secondRowHBox, placeOfferBox, amountValueCurrencyBox,
priceAsPercentageValueCurrencyBox, volumeValueCurrencyBox, priceValueCurrencyBox,
@ -1188,8 +1190,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
totalToPayTextField.setManaged(false);
addressTextField.setVisible(false);
addressTextField.setManaged(false);
qrCodeImageView.setVisible(false);
qrCodeImageView.setManaged(false);
qrCodePane.setVisible(false);
qrCodePane.setManaged(false);
balanceTextField.setVisible(false);
balanceTextField.setManaged(false);
cancelButton2.setVisible(false);
@ -1205,8 +1207,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
totalToPayTextField.setManaged(true);
addressTextField.setVisible(true);
addressTextField.setManaged(true);
qrCodeImageView.setVisible(true);
qrCodeImageView.setManaged(true);
qrCodePane.setVisible(true);
qrCodePane.setManaged(true);
balanceTextField.setVisible(true);
balanceTextField.setManaged(true);
cancelButton2.setVisible(true);
@ -1248,21 +1250,23 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
totalToPayTextField.setVisible(false);
GridPane.setMargin(totalToPayTextField, new Insets(60 + heightAdjustment, 10, 0, 0));
qrCodeImageView = new ImageView();
qrCodeImageView.setVisible(false);
qrCodeImageView.setFitHeight(150);
qrCodeImageView.setFitWidth(150);
qrCodeImageView.getStyleClass().add("qr-code");
Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow")));
qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter(
Tuple2<StackPane, ImageView> qrCodeTuple = GUIUtil.getSmallXmrQrCodePane();
qrCodePane = qrCodeTuple.first;
qrCodeImageView = qrCodeTuple.second;
Tooltip.install(qrCodePane, new Tooltip(Res.get("shared.openLargeQRWindow")));
qrCodePane.setOnMouseClicked(e -> UserThread.runAfter(
() -> new QRCodeWindow(getMoneroURI()).show(),
200, TimeUnit.MILLISECONDS));
GridPane.setRowIndex(qrCodeImageView, gridRow);
GridPane.setColumnIndex(qrCodeImageView, 1);
GridPane.setRowSpan(qrCodeImageView, 3);
GridPane.setValignment(qrCodeImageView, VPos.BOTTOM);
GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10));
gridPane.getChildren().add(qrCodeImageView);
GridPane.setRowIndex(qrCodePane, gridRow);
GridPane.setColumnIndex(qrCodePane, 1);
GridPane.setRowSpan(qrCodePane, 3);
GridPane.setValignment(qrCodePane, VPos.BOTTOM);
GridPane.setMargin(qrCodePane, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10));
gridPane.getChildren().add(qrCodePane);
qrCodePane.setVisible(false);
qrCodePane.setManaged(false);
addressTextField = addAddressTextField(gridPane, ++gridRow,
Res.get("shared.tradeWalletAddress"));

View file

@ -108,6 +108,7 @@ import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import net.glxn.qrgen.QRCode;
@ -153,6 +154,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
private Button nextButton, cancelButton1, cancelButton2;
private AutoTooltipButton takeOfferButton, fundFromSavingsWalletButton;
private ImageView qrCodeImageView;
private StackPane qrCodePane;
private BusyAnimation waitingForFundsBusyAnimation, offerAvailabilityBusyAnimation;
private Notification walletFundedNotification;
private OfferView.CloseHandler closeHandler;
@ -521,8 +523,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
totalToPayTextField.setManaged(true);
addressTextField.setVisible(true);
addressTextField.setManaged(true);
qrCodeImageView.setVisible(true);
qrCodeImageView.setManaged(true);
qrCodePane.setVisible(true);
qrCodePane.setManaged(true);
balanceTextField.setVisible(true);
balanceTextField.setManaged(true);
}
@ -930,22 +932,23 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
totalToPayTextField.setVisible(false);
totalToPayTextField.setManaged(false);
qrCodeImageView = new ImageView();
qrCodeImageView.setVisible(false);
qrCodeImageView.setManaged(false);
qrCodeImageView.setFitHeight(150);
qrCodeImageView.setFitWidth(150);
qrCodeImageView.getStyleClass().add("qr-code");
Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow")));
qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter(
Tuple2<StackPane, ImageView> qrCodeTuple = GUIUtil.getSmallXmrQrCodePane();
qrCodePane = qrCodeTuple.first;
qrCodeImageView = qrCodeTuple.second;
Tooltip.install(qrCodePane, new Tooltip(Res.get("shared.openLargeQRWindow")));
qrCodePane.setOnMouseClicked(e -> UserThread.runAfter(
() -> new QRCodeWindow(getMoneroURI()).show(),
200, TimeUnit.MILLISECONDS));
GridPane.setRowIndex(qrCodeImageView, gridRow);
GridPane.setColumnIndex(qrCodeImageView, 1);
GridPane.setRowSpan(qrCodeImageView, 3);
GridPane.setValignment(qrCodeImageView, VPos.BOTTOM);
GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10));
gridPane.getChildren().add(qrCodeImageView);
GridPane.setRowIndex(qrCodePane, gridRow);
GridPane.setColumnIndex(qrCodePane, 1);
GridPane.setRowSpan(qrCodePane, 3);
GridPane.setValignment(qrCodePane, VPos.BOTTOM);
GridPane.setMargin(qrCodePane, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10));
gridPane.getChildren().add(qrCodePane);
qrCodePane.setVisible(false);
qrCodePane.setManaged(false);
addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.tradeWalletAddress"));
addressTextField.setVisible(false);

View file

@ -17,10 +17,12 @@
package haveno.desktop.main.overlays.windows;
import haveno.common.util.Tuple2;
import haveno.common.util.Utilities;
import haveno.core.locale.Res;
import haveno.desktop.components.AutoTooltipLabel;
import haveno.desktop.main.overlays.Overlay;
import haveno.desktop.util.GUIUtil;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
@ -28,6 +30,7 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType;
import org.slf4j.Logger;
@ -38,22 +41,24 @@ import java.net.URI;
public class QRCodeWindow extends Overlay<QRCodeWindow> {
private static final Logger log = LoggerFactory.getLogger(QRCodeWindow.class);
private final ImageView qrCodeImageView;
private final StackPane qrCodePane;
private final String moneroUri;
public QRCodeWindow(String bitcoinURI) {
this.moneroUri = bitcoinURI;
public QRCodeWindow(String moneroUri) {
this.moneroUri = moneroUri;
Tuple2<StackPane, ImageView> qrCodeTuple = GUIUtil.getBigXmrQrCodePane();
qrCodePane = qrCodeTuple.first;
ImageView qrCodeImageView = qrCodeTuple.second;
final byte[] imageBytes = QRCode
.from(bitcoinURI)
.from(moneroUri)
.withSize(300, 300)
.to(ImageType.PNG)
.stream()
.toByteArray();
Image qrImage = new Image(new ByteArrayInputStream(imageBytes));
qrCodeImageView = new ImageView(qrImage);
qrCodeImageView.setFitHeight(250);
qrCodeImageView.setFitWidth(250);
qrCodeImageView.getStyleClass().add("qr-code");
qrCodeImageView.setImage(qrImage);
type = Type.Information;
width = 468;
@ -67,11 +72,11 @@ public class QRCodeWindow extends Overlay<QRCodeWindow> {
addHeadLine();
addMessage();
qrCodeImageView.setOnMouseClicked(event -> openWallet());
GridPane.setRowIndex(qrCodeImageView, ++rowIndex);
GridPane.setColumnSpan(qrCodeImageView, 2);
GridPane.setHalignment(qrCodeImageView, HPos.CENTER);
gridPane.getChildren().add(qrCodeImageView);
qrCodePane.setOnMouseClicked(event -> openWallet());
GridPane.setRowIndex(qrCodePane, ++rowIndex);
GridPane.setColumnSpan(qrCodePane, 2);
GridPane.setHalignment(qrCodePane, HPos.CENTER);
gridPane.getChildren().add(qrCodePane);
String request = moneroUri.replace("%20", " ").replace("?", "\n?").replace("&", "\n&");
Label infoLabel = new AutoTooltipLabel(Res.get("qRCodeWindow.request", request));

View file

@ -76,6 +76,7 @@ import javafx.collections.ObservableList;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
@ -97,6 +98,7 @@ import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.DirectoryChooser;
@ -1219,25 +1221,34 @@ public class GUIUtil {
}
public static ImageView getCurrencyIcon(String currencyCode) {
return getCurrencyIcon(currencyCode, 24);
}
public static ImageView getCurrencyIcon(String currencyCode, int size) {
if (currencyCode == null) return null;
ImageView iconView = new ImageView();
iconView.setFitHeight(24);
iconView.setFitWidth(24);
iconView.setFitHeight(size);
iconView.setFitWidth(size);
iconView.setSmooth(true);
iconView.setId(getImageId(currencyCode));
return iconView;
}
public static StackPane getCurrencyIconWithBorder(String currencyCode) {
return getCurrencyIconWithBorder(currencyCode, 24, 1);
}
public static StackPane getCurrencyIconWithBorder(String currencyCode, int size, double borderWidth) {
if (currencyCode == null) return null;
StackPane circleWrapper = new StackPane(getCurrencyIcon(currencyCode));
circleWrapper.setPrefSize(24, 24);
StackPane circleWrapper = new StackPane(getCurrencyIcon(currencyCode, size));
circleWrapper.setPrefSize(size, size);
circleWrapper.setMaxSize(size, size);
circleWrapper.setStyle(
"-fx-background-color: white;" +
"-fx-background-radius: 50%;" +
"-fx-border-radius: 50%;" +
"-fx-border-color: white;" +
"-fx-border-width: 1px;"
"-fx-border-color: white;" +
"-fx-border-width: " + borderWidth + "px;"
);
return circleWrapper;
}
@ -1282,4 +1293,27 @@ public class GUIUtil {
public static MaterialDesignIconView getCopyIcon() {
return new MaterialDesignIconView(MaterialDesignIcon.CONTENT_COPY, "1.35em");
}
public static Tuple2<StackPane, ImageView> getSmallXmrQrCodePane() {
return getXmrQrCodePane(150, 26, 2);
}
public static Tuple2<StackPane, ImageView> getBigXmrQrCodePane() {
return getXmrQrCodePane(250, 43, 3);
}
private static Tuple2<StackPane, ImageView> getXmrQrCodePane(int qrCodeSize, int logoSize, int logoBorderWidth) {
ImageView qrCodeImageView = new ImageView();
qrCodeImageView.setFitHeight(qrCodeSize);
qrCodeImageView.setFitWidth(qrCodeSize);
qrCodeImageView.getStyleClass().add("qr-code");
StackPane xmrLogo = GUIUtil.getCurrencyIconWithBorder(Res.getBaseCurrencyCode(), logoSize, logoBorderWidth);
StackPane qrCodePane = new StackPane(qrCodeImageView, xmrLogo);
qrCodePane.setCursor(Cursor.HAND);
qrCodePane.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
return new Tuple2<>(qrCodePane, qrCodeImageView);
}
}