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.
Related
I have added a custom toolbar (where my window actions are placed) to my application. So far everything works well. Related to the window handling I'm searching for a possibility to handle the "fullscreen got closed" event. Scenario:
App starts in windowed mode -> user clicks on (custom) toolbar button to get into fullscreen. The Toolbar will now be set its visibility to false. The users now exits fullscreen mode via button (native macOS Button to exit fullscreen) --> I need now to react for this (to set the toolbar to visible again) but cannot find a way how to do it.
main.java
MainController mc = new MainController();
Parent root = FXMLLoader.load(getClass().getResource("welcome-view.fxml"));
stage.initStyle(StageStyle.TRANSPARENT);
mc.doSwitchScenes(stage, root);
stage.show();
MainController.java
...
private String title = "Project Apollo";
private Color fillColor = TRANSPARENT;
private int minWidth = 800;
private int minHeight = 600;
...
public void btnMinimize(MouseEvent mouseEvent) {
Stage stage = (Stage)((Circle)mouseEvent.getSource()).getScene().getWindow();
// is stage minimizable into task bar. (true | false)
stage.setIconified(true);
};
public void btnCloseApp(MouseEvent mouseEvent) {
Platform.exit();
System.exit(0);
}
public void btnFullscreen(MouseEvent mouseEvent) {
Stage stage = (Stage)((Circle)mouseEvent.getSource()).getScene().getWindow();
stage.setFullScreen(true);
Scene actualScene = ((Node)mouseEvent.getSource()).getScene();
Parent hbc = (Parent) actualScene.lookup("#headerBarContainer");
if(hbc != null){
hbc.setVisible(false);
}
System.out.println("clicked FS");
}
...
The point is that at least on MacOS the window has its native os control to exit fullscreen - is it possible to target this event or at least the change of the stage size maybe?
Listen to stage.fullScreenProperty() and respond to changes:
stage.fullScreenProperty().addListener((ChangeListener) (obs,oldValue,newValue) ->
{/*TODO respond to changes in full screen */;});
I have tried searching for this many times with no luck. I am writing a software where I want the user to input their resolution before moving on to the main UI, as the main UI will change size based on the resolution given. How would I open a popup window without a button event handler and then proceed to the main application?
You can just open the popup window in your start() method:
public class MyApp extends Application {
#Override
public void start(Stage primaryStage) {
// make sure application doesn't exit when we close the dialog window:
Platform.setImplicitExit(false);
Stage popup = new Stage();
TextField resolutionField = new TextField();
// ... populate popup, etc...
popup.setOnHidden(() -> {
int resolution = Integer.parseInt(resolutionField.getText());
// now create main UI...
primaryStage.show();
});
popup.show();
}
}
I have a Webview (JavaFX 8) that load an article from Wikipedia.
I put a refresh button to allow a refresh, basically, it does another call to the load method of the webEngine of the WebView with the same URL. But about 50% of the time the article is never rendered. In this case, I can right-click on the web view to manually refresh, then it will be rendered successfully.
I tried to look at the LoadWorker state, it always says "SUCCEED"...
Below is a short runnable Test class that demonstrates my point.
public class Test1 extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
final WebView webView = new WebView();
webView.getEngine()
.load("http://fr.wikipedia.org/wiki/Sp%C3%A9cial:Page_au_hasard");
Tab tab = new Tab("webView", webView);
TabPane tabPane = new TabPane(tab);
BorderPane borderPane = new BorderPane(tabPane);
Button buttonRefresh = new Button("Refresh");
buttonRefresh.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
webView.getEngine()
.load("http://fr.wikipedia.org/wiki/Sp%C3%A9cial:Page_au_hasard");
}
});
borderPane.setBottom(buttonRefresh);
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main (String[] args) {
launch(args);
}
}
Am I doing something wrong? Does anyone know where this problem might come from?
EDIT
I added a few lines to bypass the problem, I check the header in the DOM when the state of the loadWorker becomes SUCCESS. If it is empty, I reload. Now it (looks) 100% ok, but still I am very curious why it didn't simply work all the time first.
Some thoughts that may be useful
The link http://fr.wikipedia.org/wiki/Sp%C3%A9cial:Page_au_hasard redirects to a random article (that's why my refresh button can't use webView.getEngine.reload()).
Putting a ChangeListener on the stateProperty of the workLoader to call the reload method of webEngine actually works. Each page is rendered successfully, but already rendered page will also be re-rendered, which is terrible.
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
If the button is clicked, I want to add a Label to page and fade it in via an CSS animation. I thougt, I could just create and add the label with the CSS class "hidden" attached, which has the opacity = 0 and after that remove the class and CSS will do the rest.
But i was wrong. GWT seems to execute the code in the onClick() in some kind of bulk mode -> The label gets added already without the "hidden" class. How can i prevent or do it that better? If I add/remove the "hidden" class manually in the browser, the animation works finde.
The java code looks like this:
Button submitButton = new Button("send");
submitButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Label l = new Label("test");
l.addStyleName("hidden");
RootPanel.get().add(l);
l.removeStyleName("hidden");
}
});
RootPanel.get().add(submitButton);
Das CSS sieht folgendermaßen aus:
.gwt-Label{
transition-property: opacity;
transition-duration: 1s;
}
.hidden{
opacity:0;
}
Probably you have to add some delay function before remove hidden class.
Here you have example (in JS but it's only to show):
http://jsfiddle.net/matku/PXnPZ/
$(".myElement").delay(50).queue( function(){
$(this).removeClass("hidden");
});
And another way I found on google:
http://map-notes.blogspot.com/2012/11/fade-animation.html