3
0
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:
Mike Hearn 2014-08-06 16:13:04 +02:00
parent 047c5ccd03
commit c3cef3b917
6 changed files with 102 additions and 45 deletions

View File

@ -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)

View File

@ -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() {

View File

@ -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();

View File

@ -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() {

View File

@ -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">

View File

@ -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;
}