I'm currently creating a dialog using JavaFX. The Dialog it self works very well but now I'm trying to add an input validation which warns the user when he forgets to fill out a text field.
And here comes my question: Is it possible to prevent the dialog from closing inside the Result Converter? Like this:
ButtonType buttonTypeOk = new ButtonType("Okay", ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().add(buttonTypeOk);
dialog.setResultConverter((ButtonType param) -> {
if (valid()) {
return ...
} else {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setHeaderText("Pleas fill all fields!");
alert.showAndWait();
//prevent dialog from closing
}
});
I noticed that the dialog dosn't close if an error was thrown inside the resault converter but this doesn't seems to be a good way to solve this problem.
If it isn't possible to solve the problem this way I could disable the button as described in this post. But I would prefer to keep the button enabled and display a message.
Thank you in advance !
How you are supposed to manage data validation in a dialog is actually explained in the Javadoc, I quote:
Dialog Validation / Intercepting Button Actions
In some circumstances it is desirable to prevent a dialog from closing
until some aspect of the dialog becomes internally consistent (e.g. a
form inside the dialog has all fields in a valid state). To do this,
users of the dialogs API should become familiar with the
DialogPane.lookupButton(ButtonType) method. By passing in a ButtonType
(that has already been set in the button types list), users will be
returned a Node that is typically of type Button (but this depends on
if the DialogPane.createButton(ButtonType) method has been
overridden). With this button, users may add an event filter that is
called before the button does its usual event handling, and as such
users may prevent the event handling by consuming the event. Here's a
simplified example:
final Button btOk = (Button) dlg.getDialogPane().lookupButton(ButtonType.OK);
btOk.addEventFilter(
ActionEvent.ACTION,
event -> {
// Check whether some conditions are fulfilled
if (!validateAndStore()) {
// The conditions are not fulfilled so we consume the event
// to prevent the dialog to close
event.consume();
}
}
);
In other words, you are supposed to add an event filter to your button to consume the event in case the requirements are not fulfilled which will prevent the dialog to be closed.
More details here
One other way to solve this is by using setOnCloseRequest if you don't want to relay only on the user clicking the "Okay" button. The event handler will be called when there is an external request to close the Dialog. Then the event handler can prevent dialog closing by consuming the received event.
setOnCloseRequest(e ->{
if(!valid()) {
e.consume();
}
});
Related
how can I show a dialog to stay or leave the current page with Vaadin 23, when a user clicks back button on browser?
Regards
It depends what you wish to achieve.
See this older discussion: Vaadin onbeforeunload event
Generally: use the onBeforeUnload javascript even for this
https://www.w3schools.com/tags/ev_onbeforeunload.asp
This is executed when the user would go away from your vaadin app, but not when using the back button inside your vaadin app.
For these you can use the navigation lifecycle events as documented here
https://vaadin.com/docs/latest/routing/lifecycle
Not sure if it also catches, when a user leaves your app...
Assuming you mean, that is it possible to prevent the navigation happening, you simply can't do that. If disabling back button is important for you, the only way is to enforce your users to use the application via desktop shortcut which starts the app using --app paramater (if using Chrome). This is not a limitation in Vaadin, but a general restriction in browser behavior.
There is already a possibility to handle Browser Back Button Event on Vaadin (https://vaadin.com/docs/v14/flow/routing/tutorial-routing-lifecycle):
public class SignupForm extends Div implements BeforeLeaveObserver {
#Override
public void beforeLeave(BeforeLeaveEvent event) {
if (this.hasChanges()) {
ContinueNavigationAction action = event.postpone();
ConfirmDialog.build("Are you sure you want"+
" to leave this page?")
.ifAccept(action::proceed)
.show();
}
}
private boolean hasChanges() {
// no-op implementation
return true;
}
}
This code works once but when you click on Cancel on Confirm Dialog so that you want to stay on current page and click again on Back Button on Browser, than you don't see any Confirm Dialog again... I can not understand, why...
I want to save a file before closing my JavaFX application.
This is how I'm setting up the handler in Main::start:
primaryStage.setOnCloseRequest(event -> {
System.out.println("Stage is closing");
// Save file
});
And the controller calling Stage::close when a button is pressed:
#FXML
public void exitApplication(ActionEvent event) {
((Stage)rootPane.getScene().getWindow()).close();
}
If I close the window clicking the red X on the window border (the normal way) then I get the output message "Stage is closing", which is the desired behavior.
However, when calling Controller::exitApplication the application closes without invoking the handler (there's no output).
How can I make the controller use the handler I've added to primaryStage?
If you have a look at the life-cycle of the Application class:
The JavaFX runtime does the following, in order, whenever an
application is launched:
Constructs an instance of the specified Application class
Calls the init() method
Calls the start(javafx.stage.Stage) method
Waits for the application to finish, which happens when either of the following occur:
the application calls Platform.exit()
the last window has been closed and the implicitExit attribute on Platform is true
Calls the stop() method
This means you can call Platform.exit() on your controller:
#FXML
public void exitApplication(ActionEvent event) {
Platform.exit();
}
as long as you override the stop() method on the main class to save the file.
#Override
public void stop(){
System.out.println("Stage is closing");
// Save file
}
As you can see, by using stop() you don't need to listen to close requests to save the file anymore (though you can do it if you want to prevent window closing).
Suppose you want to ask the user if he want to exit the application without saving the work. If the user choose no, you cannot avoid the application to close within the stop method. In this case you should add an EventFilter to your window for an WINDOW_CLOSE_REQUEST event.
In your start method add this code to detect the event:
(Note that calling Platform.exit(); doesn't fire the WindowEvent.WINDOW_CLOSE_REQUEST event, see below to know how to fire the event manually from a custom button)
// *** Only for Java >= 8 ****
// ==== This code detects when an user want to close the application either with
// ==== the default OS close button or with a custom close button ====
primaryStage.getScene().getWindow().addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, this::closeWindowEvent);
Then add your custom logic. In my example i use an Alert popup to ask the user if he/she want to close the application without saving.
private void closeWindowEvent(WindowEvent event) {
System.out.println("Window close request ...");
if(storageModel.dataSetChanged()) { // if the dataset has changed, alert the user with a popup
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.getButtonTypes().remove(ButtonType.OK);
alert.getButtonTypes().add(ButtonType.CANCEL);
alert.getButtonTypes().add(ButtonType.YES);
alert.setTitle("Quit application");
alert.setContentText(String.format("Close without saving?"));
alert.initOwner(primaryStage.getOwner());
Optional<ButtonType> res = alert.showAndWait();
if(res.isPresent()) {
if(res.get().equals(ButtonType.CANCEL))
event.consume();
}
}
}
The event.consume() method prevents the application from closing. Obviously you should add at least a button that permit the user to close the application to avoid the force close application by the user, that in some cases can corrupt data.
Lastly, if you have to fire the event from a custom close button, you can use this :
Window window = Main.getPrimaryStage() // Get the primary stage from your Application class
.getScene()
.getWindow();
window.fireEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSE_REQUEST));
Ahh this is a known bug in JavaFX where the Stage will not close if a modal dialog is present at the time of closing. I will link you to the bug report which I just saw today. I think it is fixed in the latest release.
Here you go:
https://bugs.openjdk.java.net/browse/JDK-8093147?jql=text%20~%20%22javafx%20re-entrant%22
resolved in 8.4 it says. I think this what you are describing.
public Stage getParentStage() {
return (Stage) getFxmlNode().getScene().getWindow();
}
btnCancel.setOnAction(e -> {
getParentStage().close();
});
I am developing a java swing desktop application. The dialog form has an ok and cancel button. When the user clicks ok button the application does some processing. How can I stop user from clicking ok again before the event on ok button has finished executing. Also, i dont want the user to able to press the cancel button till ok has finished executed. Any help will be highly appreciated.
Enablement management is an integral part of UI logic. Action helps you doing so:
Action action = new AbstractAction("myAction") {
public void actionPerformed(ActionEvent e) {
setEnabled(false);
doPerform();
setEnabled(true);
}
};
button.setAction(action);
Beware: long running task must not be executed on the EDT, so this is for short-term only, to prevent the second or so click having an effect
Edit
just noticed that you tagged the question with jsr296, then it's even easier: you can tag a method of your presentation model as #Action and bind its enabled property to a property of the model
#Action (enabledProperty == "idle")
public void processOk() {
setIdle(false);
doStuff;
setIdle(true);
}
Plus there is support (much debated, but useable) for Tasks: basically SwingWorker with fine-grained beanified life-cycle support
If you want to disable all the controls, then I'd suggest using a GlassPane. See here for more info: http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html
The easiest way is to disable the button just after clicking it , and when the action performed is complete then re-enable it.
You can disable your button like yourButton.setEnabled(false); during your processing method and enable it again when it finishes.
It seems that what you want is something like a waiting cursor.
I am working on J2ME application. I want to show alert in Form and display another Form from another class. I have tried the following method to show alert.
public void showMsg()
{
Alert success = new Alert("Data Not found.");
//success.setImage(img2);
success.addCommand(new Command("Ok", Command.OK, 0));
success.addCommand(new Command("Cancel", Command.CANCEL, 0));
success.setCommandListener(this);
success.setTimeout(Alert.FOREVER);
Display.getDisplay(parent).setCurrent(success, chapterForm);
}
After showing the alert I am jumping to another form as:
Display.getDisplay(parent).setCurrent(welcomeForm);
When I run this it don't show the alert but jump to the welComeForm. So, how can I show alert and then jump to another form.
The Alert won't advance automatically to chapterForm because you have replaced the default listener on the Alert with this. Use the commandAction() event in the CommandListener interface to get the OK or Cancel from the Alert. Then you can use Display.setCurrent(Displayable d) to show the Form you want to display.
Display.getDisplay(parent).setCurrent(welcomeForm) is most likely the reason why it don't show the alert but jump to the welComeForm. To be precise it (device) may show alert for a moment, but as soon as you invoke that setCurrent(welcomeForm), it gets momentarily overwritten by welcomeForm.
If you want welcomeForm to be dissplayed by command from alert, just
wipe out the code setCurrent(welcomeForm) from where it is now
insert that wiped-out code into this.commandAction method (this is command listener you use in your code exerpt)
A nifty solution is to start a new thread after setting the current display to the Alert, and in this new thread you can do a Thread.sleep(2000); in order to wait, and after that you display the new form.
In my GWT application, I want to ask a user confirmation when he navigates out of the current application, i.e. by entering a URL or closing the browser. This is typically done by registering a ClosingHandler and setting the desired dialog message in the onWindowClosing method. This seems to work well.
However, if the user tries to navigate say to http://www.gmail.com (by typing it in the URL bar) and hits Cancel to indicate he doesn't want to navigate, then my app keeps running but the browser's URL bar keeps indicating http://www.gmail.com. This causes a number of problems later in my application and will give the wrong result if the user bookmarks the page.
Is there a way to automatically reset the URL when the user presses Cancel?
Or, alternatively, is there a way to detect the user pressed the Cancel button? If so, is there a way to set the URL without triggering a ValueChangeEvent? (I could add some logic to prevent this, but I'd rather use a built-in mechanism if it exists.)
Not sure if this works but did you try: History.newItem(History.getToken(), false); to reset the URL? It does set the history token without triggering a new history item.
I managed to do this. It looks like GWT DeferredCommand are executed after the confirmation window has been closed. This, combined with Hilbrand's answer above, give me exactly what I want. Here is exactly what I do:
public final void onWindowClosing(Window.ClosingEvent event) {
event.setMessage(onLeaveQuestion);
DeferredCommand.addCommand( new Command() {
public void execute() {
Window.Location.replace(currentLocation);
}
});
}
Where currentLocation is obtained by calling Window.Location.getHref() every time the history token changes.
I solved this by creating a custom PlaceController and replacing the token in the url. Not an ideal solution but it works!
if (warning == null || Window.confirm(warning)) {
where = newPlace;
eventBus.fireEvent(new PlaceChangeEvent(newPlace));
currentToken = History.getToken();
} else {
// update the url when user clicks cancel in confirm popup.
History.replaceItem(currentToken, false);
}