I'm attempting to make a modal dialog similar to Swing's JOptionPane. I want to present a confirmation dialog which makes the users explicitly say "yes" before I perform some action in code.
I've shamelessly stolen this example from: https://gist.github.com/jewelsea/1887631
And modded it to be:
public class ModalConfirmDialog {
private boolean confirm = false;
private boolean isDialogOpen = false;
public boolean isOpen() {
return isDialogOpen;
}
public boolean getConfirmation() {
return confirm;
}
public void showConfirmationDialog(final Stage primaryStage) {
// initialize the confirmation dialog
final Stage dialog = new Stage(StageStyle.TRANSPARENT);
dialog.initModality(Modality.WINDOW_MODAL);
dialog.initOwner(primaryStage);
dialog.setScene(
new Scene(
HBoxBuilder.create().styleClass("modal-dialog").children(
LabelBuilder.create().text("Confirm Action?").build(),
ButtonBuilder.create().text("Yes").defaultButton(true).onAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// take action and close the dialog.
primaryStage.getScene().getRoot().setEffect(null);
dialog.close();
confirm = true;
isDialogOpen = false;
}
}).build(),
ButtonBuilder.create().text("No").cancelButton(true).onAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// abort action and close the dialog.
primaryStage.getScene().getRoot().setEffect(null);
dialog.close();
confirm = false;
isDialogOpen = false;
}
}).build()
).build()
, Color.TRANSPARENT
)
);
dialog.getScene().getStylesheets().add(getClass().getResource("modal-dialog.css").toExternalForm());
// allow the dialog to be dragged around.
final Node root = dialog.getScene().getRoot();
final Delta dragDelta = new Delta();
root.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
// record a delta distance for the drag and drop operation.
dragDelta.x = dialog.getX() - mouseEvent.getScreenX();
dragDelta.y = dialog.getY() - mouseEvent.getScreenY();
}
});
root.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
dialog.setX(mouseEvent.getScreenX() + dragDelta.x);
dialog.setY(mouseEvent.getScreenY() + dragDelta.y);
}
});
primaryStage.getScene().getRoot().setEffect(new BoxBlur());
dialog.show();
isDialogOpen = true;
}
//records relative x and y co-ordinates.
class Delta { double x, y; }
}
My problem is, when this dialog pops up, the program execution continues even though the user has not selected an option (yes or no).
I've tried setting a boolean value and checking it in a loop to know when I should check for the users choice, but this ends up causing other programs (race condition and/or just blocks everything including the dialog).
ModalConfirmDialog dialog = new ModalConfirmDialog();
dialog.showConfirmationDialog((Stage) relatedTransTable.getScene().getWindow());
while (dialog.isOpen()) {
// wait for it to close?
}
if (dialog.getConfirmation()) {
System.out.println("Confirmed choice!");
} else {
System.out.println("User denied choice!");
}
I'm unsure how to replicate JOptionPane without needing to embed Swing into my entirely JavaFX application.
Solution
Use showAndWait to display your dialog.
Shows this stage and waits for it to be hidden (closed) before returning to the caller. This method temporarily blocks processing of the current event, and starts a nested event loop to handle other events.
I'm attempting to make a modal dialog similar to Swing's JOptionPane.
The javafx.scene.control.Alert class from Java 8u40 offers functionality similar to Swing's JOptionPane and its design and implementation was heavily influenced by JOptionPane.
Current Recommendation
For basic common dialogs like alerts or confirmations, use the built-in dialog functionality in the JavaFX 8u40+ toolkits. Early access release for 8u40 is available and the dialog API there is stable and will map to the projected March 2015 production release.
If you need a specific kind of dialog box, like for tasks such as login, font selection, progress feedback and wizards, use ControlsFX (which supports a greater range of default dialog implementations than the core JavaFX platform).
If you need a completely customized dialog, then use the techniques outlined later in this answer.
Background
Don't reinvent the wheel. Use the ControlsFX library for JOptionPane type dialogs rather than writing your own implementation (and JavaFX 8u40 will introduce common dialog boxes into the platform).
The code you reference for modal dialog display is quite old now. There have been numerous features (including the addition of showAndWait) implemented both in the JavaFX core platform and 3rd party libraries, which mean that if you are doing the task today, it would probably be done in a different way.
The referenced code in the question makes use of Builders which have been deprecated in Java 8, so it is no longer recommended to use them (they no longer even show up in the JavaFX javadoc).
Code Demonstrating the built-in JavaFX 8u40+ Alert class
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class ShowAndWaitBuiltInAlert extends Application {
#Override public void start(Stage stage) {
Button showDialog = new Button("Show Dialog");
showDialog.setOnAction(event -> {
Alert dialog = new Alert(
Alert.AlertType.CONFIRMATION,
"Are you sure you want to exit the Dialog Demo Application?"
);
dialog.showAndWait()
.filter(response -> response.equals(ButtonType.OK))
.ifPresent(response -> stage.close());
});
stage.setScene(new Scene(showDialog));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
An alert extends from Dialog, so it can be customized using all the customization methods of the Dialog class.
Code Demonstrating the built-in JavaFX 8u40+ Dialog class
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class ShowAndWaitBuiltInDialog extends Application {
#Override public void start(Stage stage) {
Button showDialog = new Button("Show Dialog");
showDialog.setOnAction(e -> {
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setTitle("Dialog Demo");
dialog.setHeaderText("Confirm Exit");
dialog.setContentText("Are you sure you want to exit the Dialog Demo Application?");
ButtonType exit = new ButtonType("Exit", ButtonBar.ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(
exit, ButtonType.CANCEL
);
dialog.showAndWait()
.filter(response -> response.equals(exit))
.ifPresent(response -> stage.close());
});
stage.setScene(new Scene(showDialog));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Custom Dialog Code Demonstrating showAndWait
The following code will work in Java 8 versions predating Java 8u40.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class ShowAndWaitDialog extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Button showDialog = new Button("Show Dialog");
showDialog.setOnAction(e -> {
ConfirmationDialog dialog = new ConfirmationDialog(
"Would you like to exit the application?"
);
dialog.setY(stage.getY() + stage.getHeight() + 10);
dialog.setX(stage.getX());
dialog.showAndWait();
if (dialog.isSelected()) {
stage.close();
}
});
stage.setScene(new Scene(showDialog));
stage.show();
}
class ConfirmationDialog extends Stage {
private VBox layout = new VBox();
private ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
public boolean isSelected() {
return selected.get();
}
public ReadOnlyBooleanProperty selectedProperty() {
return selected.getReadOnlyProperty();
}
public ConfirmationDialog(String question) {
initStyle(StageStyle.UTILITY);
initModality(Modality.APPLICATION_MODAL);
layout.setSpacing(10);
layout.setPadding(new Insets(10));
createControls();
layout.getChildren().addAll(
new Label(question),
createControls()
);
setScene(new Scene(layout));
sizeToScene(); // workaround because utility stages aren't automatically sized correctly to their scene.
}
private HBox createControls() {
final Button ok = new Button("OK");
ok.setOnAction(e -> {
selected.set(true);
close();
});
final Button cancel = new Button("Cancel");
cancel.setOnAction(e -> {
selected.set(false);
close();
});
final HBox controls = new HBox(10, ok, cancel);
controls.setAlignment(Pos.CENTER_RIGHT);
return controls;
}
}
}
Update for comment question on Dec 19, 2014
Isn't this some basic UI functionality expected in every UI framework? Why did JavaFX lacked this native functionality until now?
showAndWait has been in JavaFX for about two and a half years and was added about 8 months after the initial JavaFX 2 release until showAndWait appeared as public API in JavaFX 2.2. During that period, the majority of the showAndWait functionality could be simulated using event handlers similar to the code in the asker's question.
In the more general case of built-in common dialog box API for things like alerts and confirmations (now added to Java 8u40), there is extensive history, design discussion and initial deferral reasons provided in the JavaFX issue tracker for the feature RT-12643.
dialog.setModal(true);
dialog.add(stuff here);
dialog.setVisible(true);
if i'm reading your problem correctly then the above code will make the dialog like a joptionpane & stop the user from clicking on anything but that dialog while the dialog is open
Related
So say I have a stage in JavaFX. Instead of closing the window by pressing the X close button I just hide the window or say switch to some other application on my computer. Whenever either I hide the window or switch to any other window of my PC I want the stage to close automatically.
I tried these three methods but all of these activate only when I close the window myself, not when I hide the window.
popupStage.setOnHidden(event -> Console.log("Hidden"));
popupStage.setOnHiding(event -> Console.log("Hidden"));
popupStage.setOnCloseRequest(event -> Console.log("Hidden"));
Any help would be greatly appreciated.
Thanks.
Try using the focusedProperty of the Window object (stage inherits this from Window).
You can add a listener to this property to get notified as soon as the user switches the active window.
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.stage.Stage;
public class Example extends Application {
public static void main(String args[]) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
stage.focusedProperty().addListener(this::focusChanged);
stage.setTitle("demo");
stage.show();
}
private void focusChanged(ObservableValue<? extends Boolean> property, Boolean wasFocused, Boolean isFocused) {
System.out.println("Window changed focus from " + wasFocused + " to " + isFocused);
}
}
How to call the launch() more than once in java i am given an exception as "ERROR IN MAIN:java.lang.IllegalStateException: Application launch must not be called more than once"
I have create rest cleint in my java application when request comes it call javafx and opening webview after completing webview operarion am closing javafx windows using Platform.exit() method. when second request comes am getting this error how to reslove this error.
JavaFx Application Code:
public class AppWebview extends Application {
public static Stage stage;
#Override
public void start(Stage _stage) throws Exception {
stage = _stage;
StackPane root = new StackPane();
WebView view = new WebView();
WebEngine engine = view.getEngine();
engine.load(PaymentServerRestAPI.BROWSER_URL);
root.getChildren().add(view);
engine.setJavaScriptEnabled(true);
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
engine.setOnResized(new EventHandler<WebEvent<Rectangle2D>>() {
public void handle(WebEvent<Rectangle2D> ev) {
Rectangle2D r = ev.getData();
stage.setWidth(r.getWidth());
stage.setHeight(r.getHeight());
}
});
JSObject window = (JSObject) engine.executeScript("window");
window.setMember("app", new BrowserApp());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
RestClient Method:
Calling to JavaFX application
// method 1 to lanch javafx
javafx.application.Application.launch(AppWebview.class);
// method 2 to lanch javafx
String[] arguments = new String[] {"123"};
AppWebview .main(arguments);
You can't call launch() on a JavaFX application more than once, it's not allowed.
From the javadoc:
It must not be called more than once or an exception will be thrown.
Suggestion for showing a window periodically
Just call Application.launch() once.
Keep the JavaFX runtime running in the background using Platform.setImplicitExit(false), so that JavaFX does not shutdown automatically when you hide the last application window.
The next time you need another window, wrap the window show() call in Platform.runLater(), so that the call gets executed on the JavaFX application thread.
For a short summary implementation of this approach:
See the answer by sergioFC
If you are mixing Swing you can use a JFXPanel instead of an Application, but the usage pattern will be similar to that outlined above.
For an example of the JFXPanel apprach, see Irshad Babar
s answer.
Wumpus Sample
This example is bit more complicated than it needs to be because it also involves timer tasks. However it does provide a complete stand-alone example, which might help sometimes.
import javafx.animation.PauseTransition;
import javafx.application.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.*;
// hunt the Wumpus....
public class Wumpus extends Application {
private static final Insets SAFETY_ZONE = new Insets(10);
private Label cowerInFear = new Label();
private Stage mainStage;
#Override
public void start(final Stage stage) {
// wumpus rulez
mainStage = stage;
mainStage.setAlwaysOnTop(true);
// the wumpus doesn't leave when the last stage is hidden.
Platform.setImplicitExit(false);
// the savage Wumpus will attack
// in the background when we least expect
// (at regular intervals ;-).
Timer timer = new Timer();
timer.schedule(new WumpusAttack(), 0, 5_000);
// every time we cower in fear
// from the last savage attack
// the wumpus will hide two seconds later.
cowerInFear.setPadding(SAFETY_ZONE);
cowerInFear.textProperty().addListener((observable, oldValue, newValue) -> {
PauseTransition pause = new PauseTransition(
Duration.seconds(2)
);
pause.setOnFinished(event -> stage.hide());
pause.play();
});
// when we just can't take it anymore,
// a simple click will quiet the Wumpus,
// but you have to be quick...
cowerInFear.setOnMouseClicked(event -> {
timer.cancel();
Platform.exit();
});
stage.setScene(new Scene(cowerInFear));
}
// it's so scary...
public class WumpusAttack extends TimerTask {
private String[] attacks = {
"hugs you",
"reads you a bedtime story",
"sings you a lullaby",
"puts you to sleep"
};
// the restaurant at the end of the universe.
private Random random = new Random(42);
#Override
public void run() {
// use runlater when we mess with the scene graph,
// so we don't cross the streams, as that would be bad.
Platform.runLater(() -> {
cowerInFear.setText("The Wumpus " + nextAttack() + "!");
mainStage.sizeToScene();
mainStage.show();
});
}
private String nextAttack() {
return attacks[random.nextInt(attacks.length)];
}
}
public static void main(String[] args) {
launch(args);
}
}
Update, Jan 2020
Java 9 added a new feature called Platform.startup(), which you can use to trigger startup of the JavaFX runtime without defining a class derived from Application and calling launch() on it. Platform.startup() has similar restrictions to the launch() method (you cannot call Platform.startup() more than once), so the elements of how it can be applied is similar to the launch() discussion and Wumpus example in this answer.
For a demonstration on how Platform.startup() can be used, see Fabian's answer to How to achieve JavaFX and non-JavaFX interaction?
I use something like this, similar to other answers.
private static volatile boolean javaFxLaunched = false;
public static void myLaunch(Class<? extends Application> applicationClass) {
if (!javaFxLaunched) { // First time
Platform.setImplicitExit(false);
new Thread(()->Application.launch(applicationClass)).start();
javaFxLaunched = true;
} else { // Next times
Platform.runLater(()->{
try {
Application application = applicationClass.newInstance();
Stage primaryStage = new Stage();
application.start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
try this, I tried this and found successful
#Override
public void start() {
super.start();
try {
// Because we need to init the JavaFX toolkit - which usually Application.launch does
// I'm not sure if this way of launching has any effect on anything
new JFXPanel();
Platform.runLater(new Runnable() {
#Override
public void run() {
// Your class that extends Application
new ArtisanArmourerInterface().start(new Stage());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
I have a SplitMenuButton, and I can't seem to find a way to trigger an event when the user clicks the arrow next to the button.
I would like the dropdown to fill with items from a database when the dropdown arrow is clicked.
I am not sure which event can do that, and I can not find any info on this either.
Short answer: register a listener with the showing property.
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SplitMenuButton;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class SplitMenuButtonTest extends Application {
#Override
public void start(Stage primaryStage) {
IntegerProperty count = new SimpleIntegerProperty();
SplitMenuButton splitMenuButton = new SplitMenuButton();
splitMenuButton.setText("Action");
splitMenuButton.showingProperty().addListener((obs, wasShowing, isNowShowing) -> {
if (isNowShowing) {
int c = count.get() + 1;
count.set(c);
splitMenuButton.getItems().clear();
for (int choice = 1; choice <= 3; choice++) {
MenuItem mi = new MenuItem("Choice "+choice+" (" + c + ")");
splitMenuButton.getItems().add(mi);
}
}
});
BorderPane root = new BorderPane(null, splitMenuButton, null, null, null);
primaryStage.setScene(new Scene(root, 350, 150));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Sort of as an aside, I'm not sure this is a really good idea. Database connections are typically long-running processes (i.e. long enough to be visually noticeable in a UI environment). If you run this on the FX Application Thread, then you're going to block the UI from doing anything while the data is retrieved, and that's also right at the moment the user has just tried to do something. Of course, if you run it as a background task, then the menu will popup with the previous data, and then later update once the data is downloaded. I would recommend finding a way to populate this before the user requests it.
The 'arrow' is just another button used to show the popup with the menu items.
One easy way of knowing if this arrow button is pressed is by listening to the showing property of the popup.
Once you know that the popup is showing up, you can add your items.
#Override
public void start(Stage primaryStage) {
SplitMenuButton m = new SplitMenuButton();
m.showingProperty().addListener((ov,b,b1)->{
if(b1){
System.out.println("popup visible");
MenuItem menuItem = new MenuItem("New Option");
if(m.getItems().stream().noneMatch(i->i.getText().equals(menuItem.getText()))){
menuItem.setOnAction(e -> System.out.println("New Option added"));
m.getItems().add(menuItem);
}
}
});
m.setText("Click the arrow ->");
m.getItems().add(new MenuItem("First option"));
StackPane root = new StackPane(m);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
This is something I like that I have seen in a few different softwares. I don't know where it originates from or what it really is called but here is an example of the pane system in Visual Studio.
Note how I can easily attach the pane anywhere with ease. Is such a thing possible with Javafx?
I recognize this question is old but others may be interested to know. I created a lightweight docking library for JavaFX for both proprietary and non-proprietary uses under the LGPL license.
https://github.com/RobertBColton/DockFX
There is no built-in docking framework for JavaFX 8.
There are some 3rd party solutions such as Drombler FX. I haven't used any of them.
A simple home-built system to dock and undock panels is pretty easy to create, but a comprehensive system would be quite difficult. The following code is adapted from zonski's answer to a docking framework discussion is in the Oracle JavaFX forum threads.
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class SimpleDocking extends Application {
public void start(final Stage stage) throws Exception {
final SplitPane rootPane = new SplitPane();
rootPane.setOrientation(Orientation.VERTICAL);
final FlowPane dockedArea = new FlowPane();
dockedArea.getChildren().add(new Label("Some docked content"));
final FlowPane centerArea = new FlowPane();
final Button undockButton = new Button("Undock");
centerArea.getChildren().add(undockButton);
rootPane.getItems().addAll(centerArea, dockedArea);
stage.setScene(new Scene(rootPane, 300, 300));
stage.show();
final Dialog dialog = new Dialog(stage);
undockButton.disableProperty().bind(dialog.showingProperty());
undockButton.setOnAction(actionEvent -> {
rootPane.getItems().remove(dockedArea);
dialog.setOnHidden(windowEvent -> {
rootPane.getItems().add(dockedArea);
});
dialog.setContent(dockedArea);
dialog.show(stage);
});
}
private class Dialog extends Popup {
private BorderPane root;
private Dialog(Window parent) {
root = new BorderPane();
root.setPrefSize(200, 200);
root.setStyle("-fx-border-width: 1; -fx-border-color: gray");
root.setTop(buildTitleBar());
setX(parent.getX() + 50);
setY(parent.getY() + 50);
getContent().add(root);
}
public void setContent(Node content) {
root.setCenter(content);
}
private Node buildTitleBar() {
BorderPane pane = new BorderPane();
pane.setStyle("-fx-background-color: burlywood; -fx-padding: 5");
final Delta dragDelta = new Delta();
pane.setOnMousePressed(mouseEvent -> {
dragDelta.x = getX() - mouseEvent.getScreenX();
dragDelta.y = getY() - mouseEvent.getScreenY();
});
pane.setOnMouseDragged(mouseEvent -> {
setX(mouseEvent.getScreenX() + dragDelta.x);
setY(mouseEvent.getScreenY() + dragDelta.y);
});
Label title = new Label("My Dialog");
title.setStyle("-fx-text-fill: midnightblue;");
pane.setLeft(title);
Button closeButton = new Button("X");
closeButton.setOnAction(actionEvent -> hide());
pane.setRight(closeButton);
return pane;
}
}
private static class Delta {
double x, y;
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
If you have extensive need for such a framework, you might want to look into the NetBeans platform, which is a Swing based framework into which you can embed JavaFX.
A simple docking framework for JavaFX:
https://github.com/andy-goryachev/FxDock
public void start(Stage s) throws Exception
{
// plug in custom windows and dockable panes.
FxDockFramework.setGenerator(new DemoPanes());
// load saved layout
int ct = FxDockFramework.loadLayout();
if(ct == 0)
{
// when no saved layout exists, open the first window
DemoWindow.openBrowser("https://github.com/andy-goryachev/FxDock");
}
}
As the previous answer says, JavaFX does not have built-in support for dockable tabs. There is an OpenJDK issue requesting support for draggable and dockable tabs.
A recent third-party solution that may be worth looking into is DockFX which is in active development at the time of writing (September 2015)
I am new to Java and JavaFX and I am just wondering if the webview of JavaFX can run javascript in itself apart from the communcation it has with the java code. Can I for example run a javascript alert statement in the webview when the window loads and actually get an alert in the webview as I would in a normal browser. I know that I can capture this alert event with my java code by
webEngine.setOnAlert
but I actually want javascript events to happen in the webview window itself as they would in a normal browser.
The reason that I am asking this simple question is because I am using a webview with a textarea where I want to enable spell checking. I have a javascript spell checker that works perfectly in normal browsers where I get the red underline when I type something wrong, but I want to get it to work in the JavaFX webview also.
I am thankful for any help I can get!
WebView JavaScript Callback Handler Example
Here is some sample code for displaying a JavaFX dialog based on a JavaScript trigger command. The sample code is for a JavaScript confirm handler, but code for an alert handler would function similarly.
In the sample screenshot, the yellow bar on the left will display a JavaFX label based on the result of a confirmation dialog triggered by a JavaScript function invoked from the WebView which covers the rest of the screen.
The confirmation dialog is rendered in JavaFX over the top of the WebView, preventing interaction with the WebView while the dialog is displayed. The styling of the confirmation dialog, is just a sample, it might be styled any way you wish using css, and the layout may be changed in code as well (or you could define dialog layout in FXML markup instead if you preferred).
WebViewConfirm.java
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.*;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.effect.BoxBlur;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.web.WebView;
import javafx.stage.*;
import javafx.util.Callback;
/**
* Demonstrates a modal WebView confirm box in JavaFX.
* Dialog is rendered upon a blurred background.
* Dialog is translucent.
* Requires JavaFX 2.2
* To test, run the program, then click the "Try it" button in the Result textarea.
*/
public class WebViewConfirm extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(final Stage primaryStage) {
// initialize the stage
primaryStage.setTitle("Modal Confirm Example");
final WebView webView = new WebView();
webView.getEngine().load("http://www.w3schools.com/js/tryit.asp?filename=tryjs_confirm");
// layout the stage - a vbox to show confirmation results and a webview to generate confirmations.
final VBox confirmationResults = new VBox();
confirmationResults.getStyleClass().add("confirmation-results");
confirmationResults.setMinWidth(150);
HBox layout = new HBox();
layout.getChildren().addAll(confirmationResults, webView);
primaryStage.setScene(new Scene(layout));
primaryStage.show();
primaryStage.getScene().getStylesheets().add(getClass().getResource("modal-dialog.css").toExternalForm());
// show the confirmation dialog each time a new page is loaded and
// record the confirmation result.
webView.getEngine().setConfirmHandler(new Callback<String, Boolean>() {
#Override public Boolean call(String msg) {
Boolean confirmed = confirm(primaryStage, msg);
confirmationResults.getChildren().add(new Label("Confirmed? " + confirmed));
return confirmed;
}
});
}
private Boolean confirm(final Stage parent, String msg) {
final BooleanProperty confirmationResult = new SimpleBooleanProperty();
// initialize the confirmation dialog
final Stage dialog = new Stage(StageStyle.TRANSPARENT);
dialog.initOwner(parent);
dialog.initModality(Modality.WINDOW_MODAL);
dialog.setScene(
new Scene(
HBoxBuilder.create().styleClass("modal-dialog").children(
LabelBuilder.create().text(msg).build(),
ButtonBuilder.create().text("OK").defaultButton(true).onAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// take action and close the dialog.
confirmationResult.set(true);
parent.getScene().getRoot().setEffect(null);
dialog.close();
}
}).build(),
ButtonBuilder.create().text("Cancel").cancelButton(true).onAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// abort action and close the dialog.
confirmationResult.set(false);
parent.getScene().getRoot().setEffect(null);
dialog.close();
}
}).build()
).build()
, Color.TRANSPARENT
)
);
// allow the dialog to be dragged around.
final Node root = dialog.getScene().getRoot();
final Delta dragDelta = new Delta();
root.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
// record a delta distance for the drag and drop operation.
dragDelta.x = dialog.getX() - mouseEvent.getScreenX();
dragDelta.y = dialog.getY() - mouseEvent.getScreenY();
}
});
root.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
dialog.setX(mouseEvent.getScreenX() + dragDelta.x);
dialog.setY(mouseEvent.getScreenY() + dragDelta.y);
}
});
// style and show the dialog.
dialog.getScene().getStylesheets().add(getClass().getResource("modal-dialog.css").toExternalForm());
parent.getScene().getRoot().setEffect(new BoxBlur());
dialog.showAndWait();
return confirmationResult.get();
}
// records relative x and y co-ordinates.
class Delta { double x, y; }
}
modal-dialog.css
/**
* modal-dialog.css
* place in same directory as WebViewConfirm.java
* ensure your build system copies the file to your build output directory
*/
.root {
-fx-glass-color: rgba(95, 158, 160, 0.9);
}
.modal-dialog {
-fx-padding: 20;
-fx-spacing: 10;
-fx-alignment: center;
-fx-font-size: 20;
-fx-background-color: linear-gradient(to bottom, derive(-fx-glass-color, 20%), -fx-glass-color);
-fx-border-color: derive(-fx-glass-color, -20%);
-fx-border-width: 5;
-fx-background-insets: 12;
-fx-border-insets: 10;
-fx-border-radius: 6;
-fx-background-radius: 6;
}
.modal-dialog:pressed {
-fx-cursor: move;
}
.modal-dialog .button:pressed {
-fx-cursor: default;
}
.confirmation-results {
-fx-background-color: cornsilk;
-fx-padding: 5;
}
Possible answer to your question
Your question isn't exactly clear to me, but I think if you popup a ControlsFX dialog box, you will achieve what you want. You could use either a standard Dialog (which functions like Internet Explorer's JavaScript alert dialog) or a light-weight dialog (which functions like Firefox's JavaScript alert dialog) - see the ControlsFX features page for more information.
ControlsFX is Java 8 based, but for JavaFX 2.2, there are numerous topics on StackOverflow regarding showing dialogs in JavaFX, (for example: How to create and show common dialog (Error, Warning, Confirmation) in JavaFX 2.0?). The sample code above is an example of dialog usage in JavaFX 2.2
Comments on additional points raised in your question
if the webview of JavaFX can run javascript in itself apart from the communcation it has with the java code
Yes WebView can process JavaScript.
Can I for example run a javascript alert statement in the webview when the window loads and actually get an alert in the webview as I would in a normal browser.
Yes, if you have set up an alert handler for your WebView so that it functions like a "normal browser", popping up a dialog on receiving a JavaScript alert command. Note that "normal browsers" do not modify the web page document object model based on a JavaScript alert function, instead they popup a native dialog - the dialog is not really part of the web page.
I actually want javascript events to happen in the webview window itself as they would in a normal browser.
"Normal" browsers handle alerts differently depending on their UI model. For example in Firefox the alert dialog will appear in the current browser tab window, and in Internet Explorer the alert dialog will appear above the Internet Explorer window. The JavaFX alert handler is flexible enough that you can handle the alert pretty much however you want.