walletfx: Introduce OverlayWindowController interface

Rationale:

1. Stronger typing makes code more readable and refactorable
2. Eliminates “automatic” reflection in MainController
3. Makes overlayUI field in implementing classes private
4. Is a step towards further refactoring and reusability
This commit is contained in:
Sean Gilligan 2021-09-19 12:34:52 -07:00
parent 17aeea2d75
commit 44ca7f6689
7 changed files with 73 additions and 26 deletions

View File

@ -0,0 +1,28 @@
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.walletfx.overlay;
import wallettemplate.MainController;
/**
* Interface for controllers displayed via OverlayWindow.OverlayUI
*/
public interface OverlayWindowController<T> {
/**
* @param ui The overlay UI (node, controller pair)
*/
void setOverlayUI(MainController.OverlayUI<? extends OverlayWindowController<T>> ui);
}

View File

@ -36,12 +36,12 @@ import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.util.Duration; import javafx.util.Duration;
import org.bitcoinj.walletfx.overlay.OverlayWindowController;
import org.bitcoinj.walletfx.utils.GuiUtils; import org.bitcoinj.walletfx.utils.GuiUtils;
import org.bitcoinj.walletfx.utils.TextFieldValidator; import org.bitcoinj.walletfx.utils.TextFieldValidator;
import wallettemplate.controls.ClickableBitcoinAddress; import wallettemplate.controls.ClickableBitcoinAddress;
import wallettemplate.controls.NotificationBarPane; import wallettemplate.controls.NotificationBarPane;
import org.bitcoinj.walletfx.utils.BitcoinUIModel; import org.bitcoinj.walletfx.utils.BitcoinUIModel;
import org.bitcoinj.walletfx.utils.GuiUtils;
import org.bitcoinj.walletfx.utils.easing.EasingMode; import org.bitcoinj.walletfx.utils.easing.EasingMode;
import org.bitcoinj.walletfx.utils.easing.ElasticInterpolator; import org.bitcoinj.walletfx.utils.easing.ElasticInterpolator;
@ -170,7 +170,7 @@ public class MainController {
return model.getDownloadProgressTracker(); return model.getDownloadProgressTracker();
} }
public class OverlayUI<T> { public class OverlayUI<T extends OverlayWindowController<T>> {
public Node ui; public Node ui;
public T controller; public T controller;
@ -219,22 +219,18 @@ public class MainController {
} }
@Nullable @Nullable
private OverlayUI currentOverlay; private OverlayUI<? extends OverlayWindowController<?>> currentOverlay;
public <T> OverlayUI<T> overlayUI(Node node, T controller) { public <T extends OverlayWindowController<T>> OverlayUI<T> overlayUI(Node node, T controller) {
checkGuiThread(); checkGuiThread();
OverlayUI<T> pair = new OverlayUI<>(node, controller); OverlayUI<T> pair = new OverlayUI<>(node, controller);
// Auto-magically set the overlayUI member, if it's there. controller.setOverlayUI(pair);
try {
controller.getClass().getField("overlayUI").set(controller, pair);
} catch (IllegalAccessException | NoSuchFieldException ignored) {
}
pair.show(); pair.show();
return pair; return pair;
} }
/** Loads the FXML file with the given name, blurs out the main UI and puts this one on top. */ /** Loads the FXML file with the given name, blurs out the main UI and puts this one on top. */
public <T> OverlayUI<T> overlayUI(String name) { public <T extends OverlayWindowController<T>> OverlayUI<T> overlayUI(String name) {
try { try {
checkGuiThread(); checkGuiThread();
// Load the UI from disk. // Load the UI from disk.
@ -243,13 +239,7 @@ public class MainController {
Pane ui = loader.load(); Pane ui = loader.load();
T controller = loader.getController(); T controller = loader.getController();
OverlayUI<T> pair = new OverlayUI<>(ui, controller); OverlayUI<T> pair = new OverlayUI<>(ui, controller);
// Auto-magically set the overlayUI member, if it's there. controller.setOverlayUI(pair);
try {
if (controller != null)
controller.getClass().getField("overlayUI").set(controller, pair);
} catch (IllegalAccessException | NoSuchFieldException ignored) {
ignored.printStackTrace();
}
pair.show(); pair.show();
return pair; return pair;
} catch (IOException e) { } catch (IOException e) {

View File

@ -29,6 +29,7 @@ import javafx.event.ActionEvent;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import org.bitcoinj.walletfx.overlay.OverlayWindowController;
import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.KeyParameter;
import wallettemplate.controls.BitcoinAddressValidator; import wallettemplate.controls.BitcoinAddressValidator;
import org.bitcoinj.walletfx.utils.TextFieldValidator; import org.bitcoinj.walletfx.utils.TextFieldValidator;
@ -39,7 +40,7 @@ import static org.bitcoinj.walletfx.utils.GuiUtils.*;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class SendMoneyController { public class SendMoneyController implements OverlayWindowController<SendMoneyController> {
public Button sendBtn; public Button sendBtn;
public Button cancelBtn; public Button cancelBtn;
public TextField address; public TextField address;
@ -47,11 +48,16 @@ public class SendMoneyController {
public TextField amountEdit; public TextField amountEdit;
public Label btcLabel; public Label btcLabel;
public MainController.OverlayUI overlayUI; private MainController.OverlayUI<? extends OverlayWindowController<SendMoneyController>> overlayUI;
private Wallet.SendResult sendResult; private Wallet.SendResult sendResult;
private KeyParameter aesKey; private KeyParameter aesKey;
@Override
public void setOverlayUI(MainController.OverlayUI<? extends OverlayWindowController<SendMoneyController>> ui) {
overlayUI = ui;
}
// Called by FXMLLoader // Called by FXMLLoader
public void initialize() { public void initialize() {
Coin balance = Main.bitcoin.wallet().getBalance(); Coin balance = Main.bitcoin.wallet().getBalance();

View File

@ -30,6 +30,7 @@ import javafx.scene.control.ProgressIndicator;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import org.bitcoinj.walletfx.overlay.OverlayWindowController;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.KeyParameter;
@ -44,7 +45,7 @@ import static org.bitcoinj.walletfx.utils.GuiUtils.*;
* User interface for entering a password on demand, e.g. to send money. Also used when encrypting a wallet. Shows a * User interface for entering a password on demand, e.g. to send money. Also used when encrypting a wallet. Shows a
* progress meter as we scrypt the password. * progress meter as we scrypt the password.
*/ */
public class WalletPasswordController { public class WalletPasswordController implements OverlayWindowController<WalletPasswordController> {
private static final Logger log = LoggerFactory.getLogger(WalletPasswordController.class); private static final Logger log = LoggerFactory.getLogger(WalletPasswordController.class);
@FXML HBox buttonsBox; @FXML HBox buttonsBox;
@ -54,10 +55,15 @@ public class WalletPasswordController {
@FXML GridPane widgetGrid; @FXML GridPane widgetGrid;
@FXML Label explanationLabel; @FXML Label explanationLabel;
public MainController.OverlayUI overlayUI; private MainController.OverlayUI<? extends OverlayWindowController<WalletPasswordController>> overlayUI;
private SimpleObjectProperty<KeyParameter> aesKey = new SimpleObjectProperty<>(); private SimpleObjectProperty<KeyParameter> aesKey = new SimpleObjectProperty<>();
@Override
public void setOverlayUI(MainController.OverlayUI<? extends OverlayWindowController<WalletPasswordController>> ui) {
overlayUI = ui;
}
public void initialize() { public void initialize() {
progressMeter.setOpacity(0); progressMeter.setOpacity(0);
Platform.runLater(pass1::requestFocus); Platform.runLater(pass1::requestFocus);

View File

@ -23,6 +23,7 @@ import javafx.scene.control.*;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import org.bitcoinj.crypto.*; import org.bitcoinj.crypto.*;
import org.bitcoinj.wallet.*; import org.bitcoinj.wallet.*;
import org.bitcoinj.walletfx.overlay.OverlayWindowController;
import org.slf4j.*; import org.slf4j.*;
import org.bouncycastle.crypto.params.*; import org.bouncycastle.crypto.params.*;
@ -34,7 +35,7 @@ import java.util.concurrent.*;
import org.bitcoinj.walletfx.utils.KeyDerivationTasks; import org.bitcoinj.walletfx.utils.KeyDerivationTasks;
import static org.bitcoinj.walletfx.utils.GuiUtils.*; import static org.bitcoinj.walletfx.utils.GuiUtils.*;
public class WalletSetPasswordController { public class WalletSetPasswordController implements OverlayWindowController<WalletSetPasswordController> {
private static final Logger log = LoggerFactory.getLogger(WalletSetPasswordController.class); private static final Logger log = LoggerFactory.getLogger(WalletSetPasswordController.class);
public PasswordField pass1, pass2; public PasswordField pass1, pass2;
@ -43,7 +44,7 @@ public class WalletSetPasswordController {
public Button closeButton; public Button closeButton;
public Label explanationLabel; public Label explanationLabel;
public MainController.OverlayUI overlayUI; private MainController.OverlayUI<? extends OverlayWindowController<WalletSetPasswordController>> overlayUI;
// These params were determined empirically on a top-range (as of 2014) MacBook Pro with native scrypt support, // These params were determined empirically on a top-range (as of 2014) MacBook Pro with native scrypt support,
// using the scryptenc command line tool from the original scrypt distribution, given a memory limit of 40mb. // using the scryptenc command line tool from the original scrypt distribution, given a memory limit of 40mb.
public static final Protos.ScryptParameters SCRYPT_PARAMETERS = Protos.ScryptParameters.newBuilder() public static final Protos.ScryptParameters SCRYPT_PARAMETERS = Protos.ScryptParameters.newBuilder()
@ -53,6 +54,11 @@ public class WalletSetPasswordController {
.setSalt(ByteString.copyFrom(KeyCrypterScrypt.randomSalt())) .setSalt(ByteString.copyFrom(KeyCrypterScrypt.randomSalt()))
.build(); .build();
@Override
public void setOverlayUI(MainController.OverlayUI<? extends OverlayWindowController<WalletSetPasswordController>> ui) {
overlayUI = ui;
}
public void initialize() { public void initialize() {
progressMeter.setOpacity(0); progressMeter.setOpacity(0);
} }

View File

@ -28,6 +28,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.DatePicker; import javafx.scene.control.DatePicker;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import org.bitcoinj.walletfx.overlay.OverlayWindowController;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.KeyParameter;
@ -47,7 +48,7 @@ import static org.bitcoinj.walletfx.utils.GuiUtils.informationalAlert;
import static org.bitcoinj.walletfx.utils.WTUtils.didThrow; import static org.bitcoinj.walletfx.utils.WTUtils.didThrow;
import static org.bitcoinj.walletfx.utils.WTUtils.unchecked; import static org.bitcoinj.walletfx.utils.WTUtils.unchecked;
public class WalletSettingsController { public class WalletSettingsController implements OverlayWindowController<WalletSettingsController> {
private static final Logger log = LoggerFactory.getLogger(WalletSettingsController.class); private static final Logger log = LoggerFactory.getLogger(WalletSettingsController.class);
@FXML Button passwordButton; @FXML Button passwordButton;
@ -55,10 +56,15 @@ public class WalletSettingsController {
@FXML TextArea wordsArea; @FXML TextArea wordsArea;
@FXML Button restoreButton; @FXML Button restoreButton;
public MainController.OverlayUI overlayUI; private MainController.OverlayUI<? extends OverlayWindowController<WalletSettingsController>> overlayUI;
private KeyParameter aesKey; private KeyParameter aesKey;
@Override
public void setOverlayUI(MainController.OverlayUI<? extends OverlayWindowController<WalletSettingsController>> ui) {
overlayUI = ui;
}
// Note: NOT called by FXMLLoader! // Note: NOT called by FXMLLoader!
public void initialize(@Nullable KeyParameter aesKey) { public void initialize(@Nullable KeyParameter aesKey) {
DeterministicSeed seed = Main.bitcoin.wallet().getKeyChainSeed(); DeterministicSeed seed = Main.bitcoin.wallet().getKeyChainSeed();

View File

@ -42,6 +42,7 @@ import javafx.scene.layout.Pane;
import org.bitcoinj.core.Address; import org.bitcoinj.core.Address;
import org.bitcoinj.uri.BitcoinURI; import org.bitcoinj.uri.BitcoinURI;
import org.bitcoinj.walletfx.overlay.OverlayWindowController;
import wallettemplate.Main; import wallettemplate.Main;
import org.bitcoinj.walletfx.utils.GuiUtils; import org.bitcoinj.walletfx.utils.GuiUtils;
import org.bitcoinj.walletfx.utils.QRCodeImages; import org.bitcoinj.walletfx.utils.QRCodeImages;
@ -57,7 +58,7 @@ import static javafx.beans.binding.Bindings.convert;
* address looks like a blue hyperlink. Next to it there are two icons, one that copies to the clipboard and another * address looks like a blue hyperlink. Next to it there are two icons, one that copies to the clipboard and another
* that shows a QRcode. * that shows a QRcode.
*/ */
public class ClickableBitcoinAddress extends AnchorPane { public class ClickableBitcoinAddress extends AnchorPane implements OverlayWindowController<ClickableBitcoinAddress> {
@FXML protected Label addressLabel; @FXML protected Label addressLabel;
@FXML protected ContextMenu addressMenu; @FXML protected ContextMenu addressMenu;
@FXML protected Label copyWidget; @FXML protected Label copyWidget;
@ -66,6 +67,10 @@ public class ClickableBitcoinAddress extends AnchorPane {
protected SimpleObjectProperty<Address> address = new SimpleObjectProperty<>(); protected SimpleObjectProperty<Address> address = new SimpleObjectProperty<>();
private final StringExpression addressStr; private final StringExpression addressStr;
@Override
public void setOverlayUI(MainController.OverlayUI<? extends OverlayWindowController<ClickableBitcoinAddress>> ui) {
}
public ClickableBitcoinAddress() { public ClickableBitcoinAddress() {
try { try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("bitcoin_address.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("bitcoin_address.fxml"));