diff --git a/wallettemplate/src/main/java/wallettemplate/Main.java b/wallettemplate/src/main/java/wallettemplate/Main.java index b5861de9..7f3a8562 100644 --- a/wallettemplate/src/main/java/wallettemplate/Main.java +++ b/wallettemplate/src/main/java/wallettemplate/Main.java @@ -15,6 +15,7 @@ import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.stage.Stage; +import wallettemplate.controls.NotificationBarPane; import wallettemplate.utils.GuiUtils; import wallettemplate.utils.TextFieldValidator; @@ -28,13 +29,14 @@ import static wallettemplate.utils.GuiUtils.*; public class Main extends Application { public static String APP_NAME = "WalletTemplate"; - public static NetworkParameters params = RegTestParams.get(); + public static NetworkParameters params = MainNetParams.get(); public static WalletAppKit bitcoin; public static Main instance; private StackPane uiStack; private Pane mainUI; public MainController controller; + public NotificationBarPane notificationBar; @Override public void start(Stage mainWindow) throws Exception { @@ -62,12 +64,17 @@ public class Main extends Application { FXMLLoader loader = new FXMLLoader(location); mainUI = loader.load(); controller = loader.getController(); - // Configure the window with a StackPane so we can overlay things on top of the main UI. - uiStack = new StackPane(mainUI); + // Configure the window with a StackPane so we can overlay things on top of the main UI, and a + // NotificationBarPane so we can slide messages and progress bars in from the bottom. Note that + // ordering of the construction and connection matters here, otherwise we get (harmless) CSS error + // spew to the logs. + notificationBar = new NotificationBarPane(mainUI); mainWindow.setTitle(APP_NAME); - final Scene scene = new Scene(uiStack); + uiStack = new StackPane(); + Scene scene = new Scene(uiStack); TextFieldValidator.configureScene(scene); // Add CSS that we need. scene.getStylesheets().add(getClass().getResource("wallet.css").toString()); + uiStack.getChildren().add(notificationBar); mainWindow.setScene(scene); // Make log output concise. @@ -116,7 +123,7 @@ public class Main extends Application { // last months worth or more (takes a few seconds). bitcoin.setCheckpoints(getClass().getResourceAsStream("checkpoints")); // As an example! - // bitcoin.useTor(); + bitcoin.useTor(); } bitcoin.setDownloadListener(controller.progressBarUpdater()) .setBlockingStartup(false) diff --git a/wallettemplate/src/main/java/wallettemplate/MainController.java b/wallettemplate/src/main/java/wallettemplate/MainController.java index ec85c6f9..89dd4b9e 100644 --- a/wallettemplate/src/main/java/wallettemplate/MainController.java +++ b/wallettemplate/src/main/java/wallettemplate/MainController.java @@ -3,16 +3,21 @@ package wallettemplate; import com.google.bitcoin.core.Coin; import com.google.bitcoin.core.DownloadListener; import com.google.bitcoin.utils.CoinFormat; -import javafx.animation.*; +import com.subgraph.orchid.TorClient; +import com.subgraph.orchid.TorInitializationListener; +import javafx.animation.FadeTransition; +import javafx.animation.ParallelTransition; +import javafx.animation.TranslateTransition; +import javafx.application.Platform; +import javafx.beans.property.SimpleDoubleProperty; import javafx.event.ActionEvent; import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; import javafx.util.Duration; import org.fxmisc.easybind.EasyBind; import wallettemplate.controls.ClickableBitcoinAddress; +import wallettemplate.controls.NotificationBarPane; import wallettemplate.utils.BitcoinUIModel; import wallettemplate.utils.easing.EasingMode; import wallettemplate.utils.easing.ElasticInterpolator; @@ -24,18 +29,16 @@ import static wallettemplate.Main.bitcoin; * after. This class handles all the updates and event handling for the main UI. */ public class MainController { - public ProgressBar syncProgress; - public VBox syncBox; public HBox controlsBox; public Label balance; public Button sendMoneyOutBtn; public ClickableBitcoinAddress addressControl; private BitcoinUIModel model = new BitcoinUIModel(); + private NotificationBarPane.Item syncItem; // Called by FXMLLoader. public void initialize() { - syncProgress.setProgress(-1); addressControl.setOpacity(0.0); } @@ -45,13 +48,49 @@ public class MainController { balance.textProperty().bind(EasyBind.map(model.balanceProperty(), coin -> CoinFormat.BTC.noCode().format(coin).toString())); // Don't let the user click send money when the wallet is empty. sendMoneyOutBtn.disableProperty().bind(model.balanceProperty().isEqualTo(Coin.ZERO)); - syncProgress.progressProperty().bind(model.syncProgressProperty()); + + TorClient torClient = Main.bitcoin.peerGroup().getTorClient(); + if (torClient != null) { + SimpleDoubleProperty torProgress = new SimpleDoubleProperty(-1); + String torMsg = "Initialising Tor"; + syncItem = Main.instance.notificationBar.pushItem(torMsg, torProgress); + torClient.addInitializationListener(new TorInitializationListener() { + @Override + public void initializationProgress(String message, int percent) { + Platform.runLater(() -> { + syncItem.label.set(torMsg + ": " + message); + torProgress.set(percent / 100.0); + }); + } + + @Override + public void initializationCompleted() { + Platform.runLater(() -> { + syncItem.cancel(); + showBitcoinSyncMessage(); + }); + } + }); + } else { + showBitcoinSyncMessage(); + } model.syncProgressProperty().addListener(x -> { - if (model.syncProgressProperty().get() >= 1.0) + if (model.syncProgressProperty().get() >= 1.0) { readyToGoAnimation(); + if (syncItem != null) { + syncItem.cancel(); + syncItem = null; + } + } else if (syncItem == null) { + showBitcoinSyncMessage(); + } }); } + private void showBitcoinSyncMessage() { + syncItem = Main.instance.notificationBar.pushItem("Synchronising with the Bitcoin network", model.syncProgressProperty()); + } + public void sendMoneyOut(ActionEvent event) { // Hide this UI and show the send money UI. This UI won't be clickable until the user dismisses send_money. Main.instance.overlayUI("send_money.fxml"); @@ -64,34 +103,22 @@ public class MainController { public void restoreFromSeedAnimation() { // Buttons slide out ... - TranslateTransition leave = new TranslateTransition(Duration.millis(600), controlsBox); + TranslateTransition leave = new TranslateTransition(Duration.millis(1200), controlsBox); leave.setByY(80.0); - // Sync bar slides in ... - TranslateTransition arrive = new TranslateTransition(Duration.millis(600), syncBox); - arrive.setToY(0.0); - // Slide out happens then slide in/fade happens. - SequentialTransition both = new SequentialTransition(leave, arrive); - both.setCycleCount(1); - both.setInterpolator(Interpolator.EASE_BOTH); - both.play(); + leave.play(); } public void readyToGoAnimation() { - // Sync progress bar slides out ... - TranslateTransition leave = new TranslateTransition(Duration.millis(600), syncBox); - leave.setByY(80.0); // Buttons slide in and clickable address appears simultaneously. - TranslateTransition arrive = new TranslateTransition(Duration.millis(600).multiply(2.0), controlsBox); - arrive.setInterpolator(new ElasticInterpolator(EasingMode.EASE_OUT)); + TranslateTransition arrive = new TranslateTransition(Duration.millis(1200), controlsBox); + arrive.setInterpolator(new ElasticInterpolator(EasingMode.EASE_OUT, 1, 2)); arrive.setToY(0.0); - FadeTransition reveal = new FadeTransition(Duration.millis(500), addressControl); + FadeTransition reveal = new FadeTransition(Duration.millis(1200), addressControl); reveal.setToValue(1.0); ParallelTransition group = new ParallelTransition(arrive, reveal); - // Slide out happens then slide in/fade happens. - SequentialTransition both = new SequentialTransition(leave, group); - both.setCycleCount(1); - both.setInterpolator(Interpolator.EASE_BOTH); - both.play(); + group.setDelay(NotificationBarPane.ANIM_OUT_DURATION); + group.setCycleCount(1); + group.play(); } public DownloadListener progressBarUpdater() { diff --git a/wallettemplate/src/main/java/wallettemplate/controls/NotificationBarPane.java b/wallettemplate/src/main/java/wallettemplate/controls/NotificationBarPane.java index 7bab563a..73a347bf 100644 --- a/wallettemplate/src/main/java/wallettemplate/controls/NotificationBarPane.java +++ b/wallettemplate/src/main/java/wallettemplate/controls/NotificationBarPane.java @@ -1,9 +1,11 @@ package wallettemplate.controls; +import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; +import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ObservableDoubleValue; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; @@ -14,6 +16,7 @@ import javafx.scene.control.ProgressBar; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.util.Duration; import wallettemplate.utils.GuiUtils; import wallettemplate.utils.easing.EasingMode; import wallettemplate.utils.easing.ElasticInterpolator; @@ -27,17 +30,20 @@ import javax.annotation.Nullable; * and/or a progress bar. */ public class NotificationBarPane extends BorderPane { + public static final Duration ANIM_IN_DURATION = GuiUtils.UI_ANIMATION_TIME.multiply(2); + public static final Duration ANIM_OUT_DURATION = GuiUtils.UI_ANIMATION_TIME; + private HBox bar; private Label label; private double barHeight; private ProgressBar progressBar; public class Item { - public final String label; + public final SimpleStringProperty label; @Nullable public final ObservableDoubleValue progress; public Item(String label, @Nullable ObservableDoubleValue progress) { - this.label = label; + this.label = new SimpleStringProperty(label); this.progress = progress; } @@ -77,7 +83,7 @@ public class NotificationBarPane extends BorderPane { Item item = items.get(0); bar.getChildren().clear(); - label.setText(item.label); + label.textProperty().bind(item.label); label.setMaxWidth(Double.MAX_VALUE); HBox.setHgrow(label, Priority.ALWAYS); bar.getChildren().add(label); @@ -113,8 +119,16 @@ public class NotificationBarPane extends BorderPane { timeline.stop(); timeline = null; } - KeyFrame kf = new KeyFrame(GuiUtils.UI_ANIMATION_TIME.multiply(2.0), new KeyValue(bar.prefHeightProperty(), - target, new ElasticInterpolator(EasingMode.EASE_OUT))); + Duration duration; + Interpolator interpolator; + if (target.intValue() > 0) { + interpolator = new ElasticInterpolator(EasingMode.EASE_OUT, 1, 2); + duration = ANIM_IN_DURATION; + } else { + interpolator = Interpolator.EASE_OUT; + duration = ANIM_OUT_DURATION; + } + KeyFrame kf = new KeyFrame(duration, new KeyValue(bar.prefHeightProperty(), target, interpolator)); timeline = new Timeline(kf); timeline.setOnFinished(x -> timeline = null); timeline.play(); diff --git a/wallettemplate/src/main/java/wallettemplate/utils/BitcoinUIModel.java b/wallettemplate/src/main/java/wallettemplate/utils/BitcoinUIModel.java index a7dd409e..6958b966 100644 --- a/wallettemplate/src/main/java/wallettemplate/utils/BitcoinUIModel.java +++ b/wallettemplate/src/main/java/wallettemplate/utils/BitcoinUIModel.java @@ -15,7 +15,7 @@ import java.util.Date; public class BitcoinUIModel { private SimpleObjectProperty
address = new SimpleObjectProperty<>(); private SimpleObjectProperty balance = new SimpleObjectProperty<>(Coin.ZERO); - private SimpleDoubleProperty syncProgress = new SimpleDoubleProperty(); + private SimpleDoubleProperty syncProgress = new SimpleDoubleProperty(-1); private ProgressBarUpdater syncProgressUpdater = new ProgressBarUpdater(); public BitcoinUIModel() { diff --git a/wallettemplate/src/main/resources/wallettemplate/main.fxml b/wallettemplate/src/main/resources/wallettemplate/main.fxml index e3a5db26..6d619301 100644 --- a/wallettemplate/src/main/resources/wallettemplate/main.fxml +++ b/wallettemplate/src/main/resources/wallettemplate/main.fxml @@ -35,12 +35,6 @@