mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-31 15:22:16 +00:00
Wallet template: various updates.
Backport misc improvements from PayFile. Refactor the clickable address out into a custom widget. Use FontAwesome and the wrapper class for icons instead of a custom image. QRcode support.
This commit is contained in:
parent
6ec7880079
commit
387717c6c5
@ -306,7 +306,8 @@ public class BitcoinURI {
|
||||
* @param message A message
|
||||
* @return A String containing the Bitcoin URI
|
||||
*/
|
||||
public static String convertToBitcoinURI(String address, BigInteger amount, String label, String message) {
|
||||
public static String convertToBitcoinURI(String address, @Nullable BigInteger amount, @Nullable String label,
|
||||
@Nullable String message) {
|
||||
checkNotNull(address);
|
||||
if (amount != null && amount.compareTo(BigInteger.ZERO) < 0) {
|
||||
throw new IllegalArgumentException("Amount must be positive");
|
||||
|
@ -44,5 +44,15 @@
|
||||
<artifactId>aquafx</artifactId>
|
||||
<version>0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.jensd</groupId>
|
||||
<artifactId>fontawesomefx</artifactId>
|
||||
<version>8.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.glxn</groupId>
|
||||
<artifactId>qrgen</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -1,27 +1,22 @@
|
||||
package wallettemplate;
|
||||
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.uri.BitcoinURI;
|
||||
import com.google.bitcoin.core.AbstractWalletEventListener;
|
||||
import com.google.bitcoin.core.DownloadListener;
|
||||
import com.google.bitcoin.core.Utils;
|
||||
import com.google.bitcoin.core.Wallet;
|
||||
import javafx.animation.*;
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.control.ProgressBar;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Duration;
|
||||
import wallettemplate.utils.GuiUtils;
|
||||
import wallettemplate.controls.ClickableBitcoinAddress;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.util.Date;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
@ -36,66 +31,23 @@ public class Controller {
|
||||
public ProgressBar syncProgress;
|
||||
public VBox syncBox;
|
||||
public HBox controlsBox;
|
||||
public Label requestMoneyLink;
|
||||
public Label balance;
|
||||
public ContextMenu addressMenu;
|
||||
public HBox addressLabelBox;
|
||||
public Button sendMoneyOutBtn;
|
||||
public ImageView copyWidget;
|
||||
|
||||
private Address primaryAddress;
|
||||
public ClickableBitcoinAddress addressControl;
|
||||
|
||||
// Called by FXMLLoader.
|
||||
public void initialize() {
|
||||
syncProgress.setProgress(-1);
|
||||
addressLabelBox.setOpacity(0.0);
|
||||
Tooltip tooltip = new Tooltip("Copy address to clipboard");
|
||||
Tooltip.install(copyWidget, tooltip);
|
||||
addressControl.setOpacity(0.0);
|
||||
}
|
||||
|
||||
public void onBitcoinSetup() {
|
||||
bitcoin.wallet().addEventListener(new BalanceUpdater());
|
||||
primaryAddress = bitcoin.wallet().getKeys().get(0).toAddress(Main.params);
|
||||
addressControl.setAddress(bitcoin.wallet().getKeys().get(0).toAddress(Main.params).toString());
|
||||
refreshBalanceLabel();
|
||||
}
|
||||
|
||||
public void requestMoney(MouseEvent event) {
|
||||
// User clicked on the address.
|
||||
if (event.getButton() == MouseButton.SECONDARY || (event.getButton() == MouseButton.PRIMARY && event.isMetaDown())) {
|
||||
addressMenu.show(requestMoneyLink, event.getScreenX(), event.getScreenY());
|
||||
} else {
|
||||
String uri = getURI();
|
||||
System.out.println("Opening " + uri);
|
||||
try {
|
||||
Desktop.getDesktop().browse(URI.create(uri));
|
||||
} catch (IOException e) {
|
||||
// Couldn't open wallet app.
|
||||
GuiUtils.informationalAlert("Opening wallet app failed", "Perhaps you don't have one installed?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getURI() {
|
||||
return BitcoinURI.convertToBitcoinURI(getAddress(), Utils.COIN, Main.APP_NAME, null);
|
||||
}
|
||||
|
||||
private String getAddress() {
|
||||
return primaryAddress.toString();
|
||||
}
|
||||
|
||||
public void copyWidgetClicked(MouseEvent event) {
|
||||
copyAddress(null);
|
||||
}
|
||||
|
||||
public void copyAddress(ActionEvent event) {
|
||||
// User clicked icon or menu item.
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(getAddress());
|
||||
content.putHtml(String.format("<a href='%s'>%s</a>", getURI(), getAddress()));
|
||||
clipboard.setContent(content);
|
||||
}
|
||||
|
||||
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");
|
||||
@ -122,8 +74,7 @@ public class Controller {
|
||||
// Buttons slide in and clickable address appears simultaneously.
|
||||
TranslateTransition arrive = new TranslateTransition(Duration.millis(600), controlsBox);
|
||||
arrive.setToY(0.0);
|
||||
requestMoneyLink.setText(primaryAddress.toString());
|
||||
FadeTransition reveal = new FadeTransition(Duration.millis(500), addressLabelBox);
|
||||
FadeTransition reveal = new FadeTransition(Duration.millis(500), addressControl);
|
||||
reveal.setToValue(1.0);
|
||||
ParallelTransition group = new ParallelTransition(arrive, reveal);
|
||||
// Slide out happens then slide in/fade happens.
|
||||
|
@ -24,6 +24,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static wallettemplate.utils.GuiUtils.*;
|
||||
|
||||
public class Main extends Application {
|
||||
public static String APP_NAME = "WalletTemplate";
|
||||
|
||||
@ -37,6 +39,7 @@ public class Main extends Application {
|
||||
@Override
|
||||
public void start(Stage mainWindow) throws Exception {
|
||||
instance = this;
|
||||
// Show the crash dialog for any exceptions that we don't handle and that hit the main loop.
|
||||
GuiUtils.handleCrashesOnThisThread();
|
||||
try {
|
||||
init(mainWindow);
|
||||
@ -57,7 +60,7 @@ public class Main extends Application {
|
||||
// Load the GUI. The Controller class will be automagically created and wired up.
|
||||
URL location = getClass().getResource("main.fxml");
|
||||
FXMLLoader loader = new FXMLLoader(location);
|
||||
mainUI = (Pane) loader.load();
|
||||
mainUI = loader.load();
|
||||
Controller controller = loader.getController();
|
||||
// Configure the window with a StackPane so we can overlay things on top of the main UI.
|
||||
uiStack = new StackPane(mainUI);
|
||||
@ -89,6 +92,7 @@ public class Main extends Application {
|
||||
// or progress widget to keep the user engaged whilst we initialise, but we don't.
|
||||
bitcoin.setDownloadListener(controller.progressBarUpdater())
|
||||
.setBlockingStartup(false)
|
||||
.setUserAgent(APP_NAME, "1.0")
|
||||
.startAndWait();
|
||||
// Don't make the user wait for confirmations for now, as the intention is they're sending it their own money!
|
||||
bitcoin.wallet().allowSpendingUnconfirmedTransactions();
|
||||
@ -97,40 +101,58 @@ public class Main extends Application {
|
||||
mainWindow.show();
|
||||
}
|
||||
|
||||
public class OverlayUI {
|
||||
Node ui;
|
||||
Object controller;
|
||||
public class OverlayUI<T> {
|
||||
public Node ui;
|
||||
public T controller;
|
||||
|
||||
public OverlayUI(Node ui, Object controller) {
|
||||
public OverlayUI(Node ui, T controller) {
|
||||
this.ui = ui;
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
blurOut(mainUI);
|
||||
uiStack.getChildren().add(ui);
|
||||
fadeIn(ui);
|
||||
}
|
||||
|
||||
public void done() {
|
||||
GuiUtils.fadeOutAndRemove(ui, uiStack);
|
||||
GuiUtils.blurIn(mainUI);
|
||||
checkGuiThread();
|
||||
fadeOutAndRemove(ui, uiStack);
|
||||
blurIn(mainUI);
|
||||
this.ui = null;
|
||||
this.controller = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Loads the FXML file with the given name, blurs out the main UI and puts this one on top. */
|
||||
public OverlayUI overlayUI(String name) {
|
||||
try {
|
||||
// Load the UI from disk.
|
||||
URL location = getClass().getResource(name);
|
||||
FXMLLoader loader = new FXMLLoader(location);
|
||||
Pane ui = (Pane) loader.load();
|
||||
Object controller = loader.getController();
|
||||
OverlayUI pair = new OverlayUI(ui, controller);
|
||||
public <T> OverlayUI<T> overlayUI(Node node, T controller) {
|
||||
checkGuiThread();
|
||||
OverlayUI<T> pair = new OverlayUI<T>(node, controller);
|
||||
// Auto-magically set the overlayUi member, if it's there.
|
||||
try {
|
||||
controller.getClass().getDeclaredField("overlayUi").set(controller, pair);
|
||||
} catch (IllegalAccessException | NoSuchFieldException ignored) {
|
||||
}
|
||||
GuiUtils.blurOut(mainUI);
|
||||
uiStack.getChildren().add(ui);
|
||||
GuiUtils.fadeIn(ui);
|
||||
pair.show();
|
||||
return pair;
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
try {
|
||||
checkGuiThread();
|
||||
// Load the UI from disk.
|
||||
URL location = getClass().getResource(name);
|
||||
FXMLLoader loader = new FXMLLoader(location);
|
||||
Pane ui = loader.load();
|
||||
T controller = loader.getController();
|
||||
OverlayUI<T> pair = new OverlayUI<T>(ui, controller);
|
||||
// Auto-magically set the overlayUi member, if it's there.
|
||||
try {
|
||||
controller.getClass().getDeclaredField("overlayUi").set(controller, pair);
|
||||
} catch (IllegalAccessException | NoSuchFieldException ignored) {
|
||||
}
|
||||
pair.show();
|
||||
return pair;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // Can't happen.
|
||||
|
@ -11,6 +11,7 @@ import javafx.event.ActionEvent;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import wallettemplate.controls.BitcoinAddressValidator;
|
||||
import wallettemplate.utils.GuiUtils;
|
||||
|
||||
public class SendMoneyController {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package wallettemplate;
|
||||
package wallettemplate.controls;
|
||||
|
||||
import com.google.bitcoin.core.Address;
|
||||
import com.google.bitcoin.core.AddressFormatException;
|
@ -0,0 +1,144 @@
|
||||
package wallettemplate.controls;
|
||||
|
||||
import com.google.bitcoin.uri.BitcoinURI;
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import net.glxn.qrgen.QRCode;
|
||||
import net.glxn.qrgen.image.ImageType;
|
||||
import wallettemplate.Main;
|
||||
import wallettemplate.utils.GuiUtils;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
// This control can be used with Scene Builder as long as we don't use any Java 8 features yet. Once Oracle release
|
||||
// a new Scene Builder compiled against Java 8, we'll be able to use lambdas and so on here. Until that day comes,
|
||||
// this file specifically must be recompiled against Java 7 for main.fxml to be editable visually.
|
||||
//
|
||||
// From the java directory:
|
||||
//
|
||||
// javac -cp $HOME/.m2/repository/net/glxn/qrgen/1.3/qrgen-1.3.jar:$HOME/.m2/repository/de/jensd/fontawesomefx/8.0.0/fontawesomefx-8.0.0.jar:../../../target/classes:../../../../core/target/bitcoinj-0.11-SNAPSHOT.jar -d ../../../target/classes/ -source 1.7 -target 1.7 wallettemplate/controls/ClickableBitcoinAddress.java
|
||||
|
||||
|
||||
/**
|
||||
* A custom control that implements a clickable, copyable Bitcoin address. Clicking it opens a local wallet app. The
|
||||
* 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.
|
||||
*/
|
||||
public class ClickableBitcoinAddress extends AnchorPane {
|
||||
@FXML protected Label addressLabel;
|
||||
@FXML protected ContextMenu addressMenu;
|
||||
@FXML protected Label copyWidget;
|
||||
@FXML protected Label qrCode;
|
||||
|
||||
public ClickableBitcoinAddress() {
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("bitcoin_address.fxml"));
|
||||
loader.setRoot(this);
|
||||
loader.setController(this);
|
||||
// The following line is supposed to help Scene Builder, although it doesn't seem to be needed for me.
|
||||
loader.setClassLoader(getClass().getClassLoader());
|
||||
loader.load();
|
||||
|
||||
AwesomeDude.setIcon(copyWidget, AwesomeIcon.COPY);
|
||||
Tooltip.install(copyWidget, new Tooltip("Copy address to clipboard"));
|
||||
|
||||
AwesomeDude.setIcon(qrCode, AwesomeIcon.QRCODE);
|
||||
Tooltip.install(qrCode, new Tooltip("Show a barcode scannable with a mobile phone for this address"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String uri() {
|
||||
return BitcoinURI.convertToBitcoinURI(getAddress(), null, Main.APP_NAME, null);
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return addressLabel.getText();
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
addressLabel.setText(address);
|
||||
}
|
||||
|
||||
public StringProperty addressProperty() {
|
||||
return addressLabel.textProperty();
|
||||
}
|
||||
|
||||
@FXML
|
||||
protected void copyAddress(ActionEvent event) {
|
||||
// User clicked icon or menu item.
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(getAddress());
|
||||
content.putHtml(String.format("<a href='%s'>%s</a>", uri(), getAddress()));
|
||||
clipboard.setContent(content);
|
||||
}
|
||||
|
||||
@FXML
|
||||
protected void requestMoney(MouseEvent event) {
|
||||
if (event.getButton() == MouseButton.SECONDARY || (event.getButton() == MouseButton.PRIMARY && event.isMetaDown())) {
|
||||
// User right clicked or the Mac equivalent. Show the context menu.
|
||||
addressMenu.show(addressLabel, event.getScreenX(), event.getScreenY());
|
||||
} else {
|
||||
// User left clicked.
|
||||
try {
|
||||
Desktop.getDesktop().browse(URI.create(uri()));
|
||||
} catch (IOException e) {
|
||||
GuiUtils.informationalAlert("Opening wallet app failed", "Perhaps you don't have one installed?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
protected void copyWidgetClicked(MouseEvent event) {
|
||||
copyAddress(null);
|
||||
}
|
||||
|
||||
@FXML
|
||||
protected void showQRCode(MouseEvent event) {
|
||||
// Serialize to PNG and back into an image. Pretty lame but it's the shortest code to write and I'm feeling
|
||||
// lazy tonight.
|
||||
final byte[] imageBytes = QRCode
|
||||
.from(uri())
|
||||
.withSize(320, 240)
|
||||
.to(ImageType.PNG)
|
||||
.stream()
|
||||
.toByteArray();
|
||||
Image qrImage = new Image(new ByteArrayInputStream(imageBytes));
|
||||
ImageView view = new ImageView(qrImage);
|
||||
view.setEffect(new DropShadow());
|
||||
// Embed the image in a pane to ensure the drop-shadow interacts with the fade nicely, otherwise it looks weird.
|
||||
// Then fix the width/height to stop it expanding to fill the parent, which would result in the image being
|
||||
// non-centered on the screen. Finally fade/blur it in.
|
||||
Pane pane = new Pane(view);
|
||||
pane.setMaxSize(qrImage.getWidth(), qrImage.getHeight());
|
||||
final Main.OverlayUI<ClickableBitcoinAddress> overlay = Main.instance.overlayUI(pane, this);
|
||||
view.setOnMouseClicked(new EventHandler<MouseEvent>() {
|
||||
@Override
|
||||
public void handle(MouseEvent event) {
|
||||
overlay.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@ import javafx.util.Duration;
|
||||
import java.io.IOException;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
public class GuiUtils {
|
||||
private static void runAlert(BiConsumer<Stage, AlertWindowController> setup) {
|
||||
try {
|
||||
@ -24,8 +26,8 @@ public class GuiUtils {
|
||||
Stage dialogStage = new Stage();
|
||||
dialogStage.initModality(Modality.WINDOW_MODAL);
|
||||
FXMLLoader loader = new FXMLLoader(GuiUtils.class.getResource("alert.fxml"));
|
||||
Pane pane = (Pane) loader.load();
|
||||
AlertWindowController controller = (AlertWindowController) loader.getController();
|
||||
Pane pane = loader.load();
|
||||
AlertWindowController controller = loader.getController();
|
||||
setup.accept(dialogStage, controller);
|
||||
dialogStage.setScene(new Scene(pane));
|
||||
dialogStage.showAndWait();
|
||||
@ -52,8 +54,9 @@ public class GuiUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static void informationalAlert(String message, String details) {
|
||||
Runnable r = () -> runAlert((stage, controller) -> controller.informational(stage, message, details));
|
||||
public static void informationalAlert(String message, String details, Object... args) {
|
||||
String formattedDetails = String.format(details, args);
|
||||
Runnable r = () -> runAlert((stage, controller) -> controller.informational(stage, message, formattedDetails));
|
||||
if (Platform.isFxApplicationThread())
|
||||
r.run();
|
||||
else
|
||||
@ -102,4 +105,8 @@ public class GuiUtils {
|
||||
timeline.setOnFinished(actionEvent -> node.setEffect(null));
|
||||
timeline.play();
|
||||
}
|
||||
|
||||
public static void checkGuiThread() {
|
||||
checkState(Platform.isFxApplicationThread());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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 wallettemplate.utils;
|
||||
|
||||
import javafx.application.Platform;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* A simple wrapper around {@link javafx.application.Platform#runLater(Runnable)} which will do nothing if the previous
|
||||
* invocation of runLater didn't execute on the JavaFX UI thread yet. In this way you can avoid flooding
|
||||
* the event loop if you have a background thread that for whatever reason wants to update the UI very
|
||||
* frequently. Without this class you could end up bloating up memory usage and causing the UI to stutter
|
||||
* if the UI thread couldn't keep up with your background worker.
|
||||
*/
|
||||
public class ThrottledRunLater implements Runnable {
|
||||
private final Runnable runnable;
|
||||
private final AtomicBoolean pending = new AtomicBoolean();
|
||||
|
||||
/** Created this way, the no-args runLater will execute this classes run method. */
|
||||
public ThrottledRunLater() {
|
||||
this.runnable = null;
|
||||
}
|
||||
|
||||
/** Created this way, the no-args runLater will execute the given runnable. */
|
||||
public ThrottledRunLater(Runnable runnable) {
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
public void runLater(Runnable runnable) {
|
||||
if (!pending.getAndSet(true)) {
|
||||
Platform.runLater(() -> {
|
||||
pending.set(false);
|
||||
runnable.run();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void runLater() {
|
||||
runLater(runnable != null ? runnable : this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1010 B |
@ -1,5 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?scenebuilder-classpath-element ../../../../target/classes?>
|
||||
<?scenebuilder-classpath-element ../../../../../core/target/bitcoinj-0.11-SNAPSHOT.jar?>
|
||||
<?import java.lang.*?>
|
||||
<?import java.util.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
@ -10,6 +12,8 @@
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.paint.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import wallettemplate.controls.*?>
|
||||
<?import wallettemplate.controls.ClickableBitcoinAddress ?>
|
||||
|
||||
<AnchorPane id="AnchorPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="200.0" minWidth="300.0" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="wallettemplate.Controller">
|
||||
<children>
|
||||
@ -59,31 +63,7 @@
|
||||
<Image url="@bitcoin_logo_plain.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
<HBox fx:id="addressLabelBox" alignment="CENTER_LEFT" layoutY="45.0" prefHeight="21.0" prefWidth="391.0" spacing="10.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="195.0">
|
||||
<children>
|
||||
<Label fx:id="requestMoneyLink" onMouseClicked="#requestMoney" style="-fx-cursor: hand" text="<address goes here>" textFill="BLUE" underline="true">
|
||||
<contextMenu>
|
||||
<ContextMenu fx:id="addressMenu">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#copyAddress" text="Copy to clipboard">
|
||||
<accelerator>
|
||||
<KeyCodeCombination alt="UP" code="C" control="DOWN" meta="UP" shift="UP" shortcut="UP" />
|
||||
</accelerator>
|
||||
</MenuItem>
|
||||
</items>
|
||||
</ContextMenu>
|
||||
</contextMenu>
|
||||
</Label>
|
||||
<ImageView fx:id="copyWidget" fitHeight="16.0" fitWidth="16.0" focusTraversable="true" onMouseClicked="#copyWidgetClicked" pickOnBounds="true" preserveRatio="true" smooth="true">
|
||||
<image>
|
||||
<Image url="@copy-icon.png" />
|
||||
</image>
|
||||
<HBox.margin>
|
||||
<Insets />
|
||||
</HBox.margin>
|
||||
</ImageView>
|
||||
</children>
|
||||
</HBox>
|
||||
<ClickableBitcoinAddress fx:id="addressControl" layoutY="45.0" prefHeight="21.0" prefWidth="391.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="195.0" />
|
||||
<StackPane id="connectionsListView" layoutX="14.0" layoutY="81.0" prefHeight="249.0" prefWidth="572.0">
|
||||
<children>
|
||||
<Label text="Your content goes here" />
|
||||
|
Loading…
Reference in New Issue
Block a user