mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-20 10:22:18 +01:00
Add peer info popup with tag editor and tag/nr. of trades icon
This commit is contained in:
parent
b7821da7a3
commit
212791192f
@ -117,6 +117,7 @@ public final class Preferences implements Persistable {
|
||||
private boolean useInvertedMarketPrice;
|
||||
private boolean useStickyMarketPrice = false;
|
||||
private boolean usePercentageBasedPrice = false;
|
||||
private Map<String, String> peerTagMap = new HashMap<>();
|
||||
|
||||
// Observable wrappers
|
||||
transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination);
|
||||
@ -179,6 +180,9 @@ public final class Preferences implements Persistable {
|
||||
} catch (Exception e) {
|
||||
// leave default value
|
||||
}
|
||||
|
||||
if (persisted.getPeerTagMap() != null)
|
||||
peerTagMap = persisted.getPeerTagMap();
|
||||
} else {
|
||||
setFiatCurrencies(CurrencyUtil.getAllMainFiatCurrencies());
|
||||
fiatCurrencies = new ArrayList<>(fiatCurrenciesAsObservable);
|
||||
@ -379,6 +383,11 @@ public final class Preferences implements Persistable {
|
||||
storage.queueUpForSave();
|
||||
}
|
||||
|
||||
public void setTagForPeer(String hostName, String tag) {
|
||||
peerTagMap.put(hostName, tag);
|
||||
storage.queueUpForSave();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter
|
||||
@ -504,6 +513,10 @@ public final class Preferences implements Persistable {
|
||||
return usePercentageBasedPrice;
|
||||
}
|
||||
|
||||
public Map<String, String> getPeerTagMap() {
|
||||
return peerTagMap;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
|
@ -1020,6 +1020,22 @@ textfield */
|
||||
-fx-effect: dropshadow(gaussian, #666, 12, 0, -1, 3);
|
||||
}
|
||||
|
||||
#peer-info-popup-bg {
|
||||
-fx-font-size: 11;
|
||||
-fx-text-fill: #3c3c3c;
|
||||
-fx-background-color: linear-gradient(to bottom, #fafafa, #f1f1f1);
|
||||
-fx-background-radius: 3 3 3 3;
|
||||
-fx-background-insets: 5 5 20 20;
|
||||
-fx-effect: dropshadow(gaussian, #666, 12, 0, -1, 3);
|
||||
}
|
||||
|
||||
#peer-info-popup-headline {
|
||||
-fx-font-size: 13;
|
||||
-fx-font-weight: bold;
|
||||
-fx-text-fill: #333;
|
||||
}
|
||||
|
||||
|
||||
.popup-icon-information {
|
||||
-fx-text-fill: #ddd;
|
||||
}
|
||||
|
@ -26,6 +26,10 @@
|
||||
-fx-image: url("../../../images/green_circle.png");
|
||||
}
|
||||
|
||||
#image-blue_circle {
|
||||
-fx-image: url("../../../images/blue_circle.png");
|
||||
}
|
||||
|
||||
#image-remove {
|
||||
-fx-image: url("../../../images/remove.png");
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
if (DevFlags.DEV_MODE) {
|
||||
amount.set("0.0001");
|
||||
minAmount.set(amount.get());
|
||||
price.set("400");
|
||||
price.set("700");
|
||||
volume.set("0.04");
|
||||
|
||||
setAmountToModel();
|
||||
|
@ -123,7 +123,7 @@ public abstract class Overlay<T extends Overlay> {
|
||||
protected String truncatedMessage;
|
||||
private StaticProgressIndicator progressIndicator;
|
||||
private boolean showProgressIndicator;
|
||||
private Button actionButton;
|
||||
protected Button actionButton;
|
||||
protected Label headLineLabel;
|
||||
protected String dontShowAgainId;
|
||||
protected String dontShowAgainText;
|
||||
|
@ -0,0 +1,250 @@
|
||||
package io.bitsquare.gui.main.overlays.editor;
|
||||
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.main.overlays.Overlay;
|
||||
import io.bitsquare.gui.util.FormBuilder;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.animation.Interpolator;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Camera;
|
||||
import javafx.scene.PerspectiveCamera;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.transform.Rotate;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Window;
|
||||
import javafx.util.Duration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class PeerInfoWithTagEditor extends Overlay<PeerInfoWithTagEditor> {
|
||||
private static final Logger log = LoggerFactory.getLogger(PeerInfoWithTagEditor.class);
|
||||
private InputTextField inputTextField;
|
||||
private Point2D position;
|
||||
|
||||
private static PeerInfoWithTagEditor INSTANCE;
|
||||
private Consumer<String> saveHandler;
|
||||
private String hostName;
|
||||
private int numTrades;
|
||||
private ChangeListener<Boolean> focusListener;
|
||||
|
||||
|
||||
public PeerInfoWithTagEditor() {
|
||||
width = 400;
|
||||
type = Type.Undefined;
|
||||
if (INSTANCE != null)
|
||||
INSTANCE.hide();
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
public PeerInfoWithTagEditor onSave(Consumer<String> saveHandler) {
|
||||
this.saveHandler = saveHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PeerInfoWithTagEditor position(Point2D position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PeerInfoWithTagEditor hostName(String hostName) {
|
||||
this.hostName = hostName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PeerInfoWithTagEditor numTrades(int numTrades) {
|
||||
this.numTrades = numTrades;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
headLine("Peer info");
|
||||
actionButtonText("Save");
|
||||
createGridPane();
|
||||
addHeadLine();
|
||||
addContent();
|
||||
addCloseButton();
|
||||
applyStyles();
|
||||
onShow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShow() {
|
||||
super.display();
|
||||
|
||||
if (stage != null) {
|
||||
focusListener = (observable, oldValue, newValue) -> {
|
||||
if (!newValue)
|
||||
hide();
|
||||
};
|
||||
stage.focusedProperty().addListener(focusListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
animateHide(() -> {
|
||||
removeEffectFromBackground();
|
||||
|
||||
if (stage != null)
|
||||
stage.hide();
|
||||
else
|
||||
log.warn("Stage is null");
|
||||
|
||||
cleanup();
|
||||
onHidden();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHidden() {
|
||||
INSTANCE = null;
|
||||
|
||||
if (stage != null && focusListener != null)
|
||||
stage.focusedProperty().removeListener(focusListener);
|
||||
}
|
||||
|
||||
protected void addContent() {
|
||||
FormBuilder.addLabelTextField(gridPane, ++rowIndex, "Onion address:", hostName);
|
||||
FormBuilder.addLabelTextField(gridPane, ++rowIndex, "Number of completed trades:", String.valueOf(numTrades));
|
||||
inputTextField = FormBuilder.addLabelInputTextField(gridPane, ++rowIndex, "Set tag for that peer:").second;
|
||||
HashMap<String, String> peerTagMap = Preferences.INSTANCE.getPeerTagMap();
|
||||
String tag = peerTagMap.containsKey(hostName) ? peerTagMap.get(hostName) : "";
|
||||
inputTextField.setText(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addHeadLine() {
|
||||
super.addHeadLine();
|
||||
GridPane.setHalignment(headLineLabel, HPos.CENTER);
|
||||
}
|
||||
|
||||
protected void setupKeyHandler(Scene scene) {
|
||||
scene.setOnKeyPressed(e -> {
|
||||
if (e.getCode() == KeyCode.ESCAPE) {
|
||||
e.consume();
|
||||
doClose();
|
||||
}
|
||||
if (e.getCode() == KeyCode.ENTER) {
|
||||
e.consume();
|
||||
save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void animateHide(Runnable onFinishedHandler) {
|
||||
if (Preferences.INSTANCE.getUseAnimations()) {
|
||||
double duration = getDuration(300);
|
||||
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
|
||||
|
||||
gridPane.setRotationAxis(Rotate.X_AXIS);
|
||||
Camera camera = gridPane.getScene().getCamera();
|
||||
gridPane.getScene().setCamera(new PerspectiveCamera());
|
||||
|
||||
Timeline timeline = new Timeline();
|
||||
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
|
||||
keyFrames.add(new KeyFrame(Duration.millis(0),
|
||||
new KeyValue(gridPane.rotateProperty(), 0, interpolator),
|
||||
new KeyValue(gridPane.opacityProperty(), 1, interpolator)
|
||||
));
|
||||
keyFrames.add(new KeyFrame(Duration.millis(duration),
|
||||
new KeyValue(gridPane.rotateProperty(), -90, interpolator),
|
||||
new KeyValue(gridPane.opacityProperty(), 0, interpolator)
|
||||
));
|
||||
timeline.setOnFinished(event -> {
|
||||
gridPane.setRotate(0);
|
||||
gridPane.setRotationAxis(Rotate.Z_AXIS);
|
||||
gridPane.getScene().setCamera(camera);
|
||||
onFinishedHandler.run();
|
||||
});
|
||||
timeline.play();
|
||||
} else {
|
||||
onFinishedHandler.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void animateDisplay() {
|
||||
if (Preferences.INSTANCE.getUseAnimations()) {
|
||||
double startY = -160;
|
||||
double duration = getDuration(400);
|
||||
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
|
||||
double height = gridPane.getPrefHeight();
|
||||
Timeline timeline = new Timeline();
|
||||
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
|
||||
keyFrames.add(new KeyFrame(Duration.millis(0),
|
||||
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
|
||||
new KeyValue(gridPane.translateYProperty(), startY, interpolator)
|
||||
));
|
||||
|
||||
keyFrames.add(new KeyFrame(Duration.millis(duration),
|
||||
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
|
||||
new KeyValue(gridPane.translateYProperty(), 0, interpolator)
|
||||
));
|
||||
|
||||
timeline.play();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createGridPane() {
|
||||
super.createGridPane();
|
||||
gridPane.setPadding(new Insets(15, 15, 30, 30));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addCloseButton() {
|
||||
buttonDistance = 10;
|
||||
super.addCloseButton();
|
||||
|
||||
actionButton.setOnAction(event -> save());
|
||||
}
|
||||
|
||||
private void save() {
|
||||
hide();
|
||||
if (saveHandler != null && inputTextField != null)
|
||||
saveHandler.accept(inputTextField.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyStyles() {
|
||||
gridPane.setId("peer-info-popup-bg");
|
||||
if (headLineLabel != null)
|
||||
headLineLabel.setId("peer-info-popup-headline");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setModality() {
|
||||
stage.initOwner(owner.getScene().getWindow());
|
||||
stage.initModality(Modality.NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layout() {
|
||||
Window window = owner.getScene().getWindow();
|
||||
stage.setX(Math.round(window.getX() + position.getX() - width));
|
||||
stage.setY(Math.round(window.getY() + position.getY()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addEffectToBackground() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeEffectFromBackground() {
|
||||
}
|
||||
}
|
@ -18,7 +18,10 @@
|
||||
package io.bitsquare.gui.util;
|
||||
|
||||
import com.sun.javafx.tk.quantum.QuantumToolkit;
|
||||
import io.bitsquare.gui.main.overlays.editor.PeerInfoWithTagEditor;
|
||||
import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
@ -34,6 +37,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
@ -73,7 +77,7 @@ public class ImageUtil {
|
||||
return isRetina;
|
||||
}
|
||||
|
||||
public static Node getIdentIcon(String hostName, String tooltipText, int numPastTrades) {
|
||||
public static Node getIdentIcon(String hostName, String tooltipText, int numTrades) {
|
||||
if (!hostName.isEmpty()) {
|
||||
// for testing locally we use a random hostname to get dif. colors
|
||||
if (hostName.startsWith("localhost"))
|
||||
@ -89,40 +93,65 @@ public class ImageUtil {
|
||||
int index = (intValue % maxIndices) + 1;
|
||||
double saturation = (intValue % 1000) / 1000d;
|
||||
int red = (intValue >> 8) % 256;
|
||||
int green = (intValue >> 16) % 64; // we use green for marking repeated trades, so avoid it in main bg color
|
||||
int green = (intValue >> 16) % 256;
|
||||
int blue = (intValue >> 24) % 256;
|
||||
|
||||
ImageView iconView = new ImageView();
|
||||
iconView.setId("avatar_" + index);
|
||||
iconView.setScaleX(intValue % 2 == 0 ? 1d : -1d);
|
||||
double size = 26;
|
||||
Group iconGroup = new Group();
|
||||
|
||||
Group group = new Group();
|
||||
Map<String, String> peerTagMap = Preferences.INSTANCE.getPeerTagMap();
|
||||
String tag;
|
||||
if (peerTagMap.containsKey(hostName)) {
|
||||
tag = peerTagMap.get(hostName);
|
||||
Tooltip.install(group, new Tooltip(tooltipText + "\nTag: " + tag));
|
||||
} else {
|
||||
tag = "";
|
||||
Tooltip.install(group, new Tooltip(tooltipText));
|
||||
}
|
||||
|
||||
Pane numTradesPane = new Pane();
|
||||
numTradesPane.relocate(16, 16);
|
||||
numTradesPane.setMouseTransparent(true);
|
||||
if (numPastTrades > 0) {
|
||||
Label label = new Label(numPastTrades < 10 ? String.valueOf(numPastTrades) : "★");
|
||||
label.relocate(5, 1);
|
||||
label.setId("ident-num-label");
|
||||
ImageView icon = new ImageView();
|
||||
icon.setLayoutX(0.5);
|
||||
icon.setId("image-green_circle");
|
||||
numTradesPane.getChildren().addAll(icon, label);
|
||||
}
|
||||
ImageView icon = new ImageView();
|
||||
Label label = new Label();
|
||||
label.relocate(5, 1);
|
||||
label.setId("ident-num-label");
|
||||
icon.setLayoutX(0.5);
|
||||
numTradesPane.getChildren().addAll(icon, label);
|
||||
|
||||
Color color = Color.rgb(red, green, blue);
|
||||
color = color.deriveColor(1, saturation, 1, 1); // reduce saturation
|
||||
updatePeerInfoIcon(numTrades, tag, numTradesPane, label, icon);
|
||||
|
||||
Canvas bg = new Canvas(size, size);
|
||||
GraphicsContext gc = bg.getGraphicsContext2D();
|
||||
|
||||
Color color = Color.rgb(red, green, blue);
|
||||
color = color.deriveColor(1, saturation, 1, 1); // reduce saturation
|
||||
gc.setFill(color);
|
||||
gc.fillOval(0, 0, size, size);
|
||||
bg.setLayoutY(1);
|
||||
iconGroup.getChildren().addAll(bg, iconView, numTradesPane);
|
||||
group.getChildren().addAll(bg, iconView, numTradesPane);
|
||||
|
||||
Tooltip.install(iconGroup, new Tooltip(tooltipText));
|
||||
return iconGroup;
|
||||
final String finalHostName = hostName;
|
||||
group.setOnMouseClicked(e -> {
|
||||
new PeerInfoWithTagEditor()
|
||||
.hostName(finalHostName)
|
||||
.numTrades(numTrades)
|
||||
.position(group.localToScene(new Point2D(0, 0)))
|
||||
.onSave(newTag -> {
|
||||
Preferences.INSTANCE.setTagForPeer(finalHostName, newTag);
|
||||
if (!newTag.isEmpty())
|
||||
Tooltip.install(group, new Tooltip(tooltipText + "\nTag: " + newTag));
|
||||
else
|
||||
Tooltip.install(group, new Tooltip(tooltipText));
|
||||
updatePeerInfoIcon(numTrades, newTag, numTradesPane, label, icon);
|
||||
})
|
||||
.show();
|
||||
});
|
||||
|
||||
return group;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
@ -132,4 +161,20 @@ public class ImageUtil {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void updatePeerInfoIcon(int numTrades, String tag, Pane numTradesPane, Label label, ImageView icon) {
|
||||
if (numTrades == 0 && tag.isEmpty())
|
||||
label.setText("");
|
||||
else if (numTrades == 0)
|
||||
label.setText(tag.substring(0, 1));
|
||||
else if (numTrades > 9)
|
||||
label.setText("★");
|
||||
else
|
||||
label.setText(String.valueOf(numTrades));
|
||||
|
||||
numTradesPane.setVisible(!label.getText().isEmpty());
|
||||
numTradesPane.setManaged(numTradesPane.isVisible());
|
||||
|
||||
icon.setId(numTrades > 0 ? "image-green_circle" : "image-blue_circle");
|
||||
}
|
||||
}
|
||||
|
BIN
gui/src/main/resources/images/blue_circle.png
Normal file
BIN
gui/src/main/resources/images/blue_circle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 674 B |
BIN
gui/src/main/resources/images/blue_circle@2x.png
Normal file
BIN
gui/src/main/resources/images/blue_circle@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
Loading…
Reference in New Issue
Block a user