mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 06:44:16 +00:00
WalletTemplate: New sync UI using the notification bar system. Wire up Tor initialisation so startup is less boring to look at.
This commit is contained in:
parent
047c5ccd03
commit
c3cef3b917
@ -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)
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -15,7 +15,7 @@ import java.util.Date;
|
||||
public class BitcoinUIModel {
|
||||
private SimpleObjectProperty<Address> address = new SimpleObjectProperty<>();
|
||||
private SimpleObjectProperty<Coin> balance = new SimpleObjectProperty<>(Coin.ZERO);
|
||||
private SimpleDoubleProperty syncProgress = new SimpleDoubleProperty();
|
||||
private SimpleDoubleProperty syncProgress = new SimpleDoubleProperty(-1);
|
||||
private ProgressBarUpdater syncProgressUpdater = new ProgressBarUpdater();
|
||||
|
||||
public BitcoinUIModel() {
|
||||
|
@ -35,12 +35,6 @@
|
||||
<Label font="$x1" text="BTC" textFill="$x2" />
|
||||
</children>
|
||||
</HBox>
|
||||
<VBox fx:id="syncBox" maxHeight="-1.0" prefHeight="46.0" prefWidth="243.0" spacing="10.0" translateY="0.0" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0">
|
||||
<children>
|
||||
<Label text="Synchronizing with network ..." />
|
||||
<ProgressBar fx:id="syncProgress" maxWidth="1.7976931348623157E308" prefWidth="200.0" progress="0.0" />
|
||||
</children>
|
||||
</VBox>
|
||||
<HBox fx:id="controlsBox" alignment="TOP_LEFT" fillHeight="true" layoutX="14.0" minHeight="16.0" prefHeight="16.0" prefWidth="205.0" spacing="10.0" translateY="60.0" visible="true" AnchorPane.bottomAnchor="29.0">
|
||||
<children>
|
||||
<Button id="connectBtn" defaultButton="true" mnemonicParsing="false" text="Primary">
|
||||
|
@ -14,3 +14,18 @@
|
||||
.root-pane {
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
.info-bar {
|
||||
-fx-background-color: linear-gradient(to bottom, black, darkslategray);
|
||||
-fx-padding: 10;
|
||||
}
|
||||
.info-bar > Label {
|
||||
-fx-text-fill: white;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
.info-bar > .progress-bar > .bar {
|
||||
-fx-padding: 8;
|
||||
}
|
||||
.info-bar > .progress-bar > .track {
|
||||
-fx-opacity: 0.0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user