I work on desktop application based on JDK 8 and JavaFX.
I created custom dialog class with 2 buttons(finish and cancel). My goal is to return the list of strings added in dialog (after clicking finish button, dialog returns list. Cancel makes return empty list).
I have problem, beacause function showAndWait return type of button which I clicked ('ButtonType.FINISH' or 'ButtonType.CANCEL'). My goal is to override default action on finish and close button and I want to return list instead of return button type.
It's always possible to create custom buttons, however, it would be better to use those already provided by JavaFX.
In response, you can use any of the JVM languages (Java/Kotlin/Scala).
Code:
class MyDialog : Dialog<MutableList<String>>() {
val listToReturn: MutableList<String> = mutableListOf()
init {
val dialogPane: DialogPane = this.dialogPane
dialogPane.buttonTypes.addAll(ButtonType.FINISH, ButtonType.CANCEL)
}
}
val myDialog: MyDialog = MyDialog()
// here I got ButtonType ('ButtonType.FINISH' or 'ButtonType.CANCEL'), not list of string
myDialog.showAndWait().ifPresent { list -> println(list) }
You need to use a result converter
public class MyDialog extends Dialog<List<String>> {
ArrayList<String> returnList = new ArrayList<>();
public MyDialog() {
returnList.add("test 1");
returnList.add("test 2");
this.getDialogPane().getButtonTypes().addAll(ButtonType.FINISH, ButtonType.CANCEL);
setResultConverter(dialogButton -> {
if (dialogButton == ButtonType.FINISH) {
return returnList;
}
return new ArrayList<>();
});
}
}
and for the application side
public class main extends Application {
public static void main (String [] args) {
launch();
}
#Override
public void start(Stage primaryStage) {
MyDialog myDialog = new MyDialog();
myDialog.showAndWait().ifPresent(System.out::println);
}
}
Related
I would like to have this functionality in my program:
I will have a user input field. When the user pressed the button, it will be added to the list, and input will be shown to the user.
The problem is, I would like to deselect/remove those input if the user wants. I could not achieve this.
Here is the code I have written so far, I have removed some functionality unnecessary for the question's scope:
public class AddUserInput extends VerticalLayout{
// The user input will be added to the this list
// later, this list will be sent to the server for some verification
private List<String> emails;
private HorizontalLayout content;
private VerticalLayout rows;
// user input field
private TextField emailField = new TextField("Enter email address");
public AddUserInput() {
content = new HorizontalLayout();
rows = new VerticalLayout();
content.setMargin(true);
Button addToListButton= new Button("Add to list");
addToListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
// When the user clicks add to list button
// The raw input will be added to the emails list
// The UI component is added to 'rows' component
rows.addComponent(addNewRow(emailField.getValue()));
}
});
content.addComponents(emailField, addToListButton, rows);
addComponent(content);
}
public Component addNewRow(String email){
HorizontalLayout newRow = new HorizontalLayout();
Button deleteRowButton = new Button("-");
deleteRowButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
// I can delete from the UI by using the code below
newRow.removeAllComponents();
rows.removeComponent(newRow);
// How to remove from the email list???
}
});
emails.add(emailField.getValue());
Label lastEmail = new Label(emailField.getValue());
emailField.clear();
newRow.addComponents(lastEmail,deleteRowButton);
return newRow;
}
}
Is there any component/library that does this functionality?
I only need a text field, and adding the input to the list, and removing the list item if a user wants to.
The visualization of the code above:
You could use the NativeSelect component for managing the entered Strings.
I modified your AddUserInput-Component to use a NativeSelect and a corresponding DataProvider:
public class AddUserInput extends VerticalLayout {
private HorizontalLayout content = new HorizontalLayout();;
private NativeSelect<String> select = new NativeSelect<>("The List");
private ListDataProvider<String> dataProvider = DataProvider.ofCollection(new ArrayList<>());
private Button addToListButton= new Button("Add to list");
private Button deleteFromListButton = new Button("-");
private TextField emailField = new TextField("Enter email address");
public AddUserInput() {
select.setVisibleItemCount(5);
select.setWidth("100px");
select.setDataProvider(dataProvider);
select.setEmptySelectionAllowed(false);
deleteFromListButton.setEnabled(false);
content.setMargin(true);
addToListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
addEmailToList(emailField.getValue());
}
});
deleteFromListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
select.getSelectedItem().ifPresent(selectedItem -> removeSelectedEmailFromList());
}
});
select.addValueChangeListener(new HasValue.ValueChangeListener<String>() {
#Override
public void valueChange(HasValue.ValueChangeEvent<String> valueChangeEvent) {
deleteFromListButton.setEnabled(select.getSelectedItem().isPresent());
}
});
content.addComponents(emailField, addToListButton, select, deleteFromListButton);
addComponent(content);
}
private void addEmailToList(String email){
dataProvider.getItems().add(email);
select.getDataProvider().refreshAll();
emailField.clear();
}
private void removeSelectedEmailFromList(){
select.getSelectedItem().ifPresent(selectedItem -> dataProvider.getItems().remove(selectedItem));
select.setSelectedItem(dataProvider.getItems().isEmpty() ? null : dataProvider.getItems().iterator().next());
select.getDataProvider().refreshAll();
}
}
It looks like the following:
Would that be a possible option for you?
how can I refactor the following code so that only the code in deleteButton.setOnAction(deleteEvent -> {//only this code varies} changes. Everything else will stay the same but the block of code in the lambda expression varies from time to time when I call the class from another class. The block of code that goes through the lambda expression is supposed to be a void method.
public class A {
public void test() {
// ensure that user can't close the alert
Stage primaryStage = (Stage) RootLayoutController.getRootLayout().getScene().getWindow();
JFXAlert<javafx.scene.control.ButtonType> alert = new JFXAlert<>(primaryStage);
alert.initModality(Modality.APPLICATION_MODAL);
alert.setOverlayClose(false);
//create font awesome icon
String ICON = "\uf071";
Label labelIcon = new Label(ICON);
labelIcon.setStyle("-fx-font-family: 'FontAwesome'; -fx-font-size: 60px; -fx-text-fill: #D34336;");
labelIcon.setPadding(new Insets(0,5,0,0));
// Create the content of the JFXAlert with JFXDialogLayout
JFXDialogLayout layout = new JFXDialogLayout();
Label labelHeading = new Label("Alert Notification");
Label labelBody = new Label("Are you sure you want to delete this?");
layout.setHeading(labelHeading);
layout.setBody(new VBox(new HBox(labelIcon, labelBody)));
// Buttons get added into the actions section of the layout.
JFXButton deleteButton = new JFXButton("Delete");
deleteButton.setDefaultButton(true);
deleteButton.setOnAction(deleteEvent -> {
//only this block of code changes
alert.hideWithAnimation();
});
JFXButton cancelButton = new JFXButton("Cancel");
cancelButton.setCancelButton(true);
cancelButton.setOnAction(closeEvent -> alert.hideWithAnimation());
layout.setActions(deleteButton, cancelButton);
alert.setContent(layout);
alert.showAndWait();
}
}
It is not entirely clear from your question what you are trying to accomplish, but I will take a wild stab at it.
If you are looking to be able to pass a code block to the deleteButton.setOnAction() method, you could use an Interface and pass implementations of that interface to the A class. Then just pass that reference to an internal method for the onAction lambda.
Here is a very quick example of how you could do something like this:
Main.java:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Action button
Button btnDoSomething = new Button("Do something...");
btnDoSomething.setOnAction(e -> doTheThings(new ImplDoSomething()));
Button btnDoSomethingElse = new Button("Do something else...");
btnDoSomethingElse.setOnAction(e -> doTheThings(new ImplDoSomethingElse()));
VBox mainPane = new VBox(5);
mainPane.setAlignment(Pos.CENTER);
mainPane.setPadding(new Insets(10));
mainPane.getChildren().addAll(btnDoSomething, btnDoSomethingElse);
primaryStage.setScene(new Scene(mainPane));
primaryStage.show();
}
private void doTheThings(IParameterMethod parameterMethod) {
parameterMethod.call();
}
}
The IParameterMethod.java Interface:
public interface IParameterMethod {
void call();
}
Then you can create as many classes as you like that implement that interface, each with their own call() method, allowing you to execute different code.
ImplDoSomething.java
public class ImplDoSomething implements IParameterMethod {
#Override
public void call() {
System.out.println("Doing something!");
}
}
ImplDoSomethingElse.java:
public class ImplDoSomethingElse implements IParameterMethod {
#Override
public void call() {
System.out.println("Doing something else!");
}
}
This should be easily adapted for your project.
I have 2 classes, one class is a JFrame (MainUIHolder.java) and the other class is a JDialog (EditValuationsDialog.java). MainUIHolder can call EditValuationsDialog on button click event.
Once EditValuationsDialog is open, user can enter data in its fields and press its "Add" button. OK, here is the issue now. Once the user press the "Add" button, the EditValuationsDialog should inform that to the MainUIHolder.
Below is the code.
MainUIHolder
Action edit = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
JTable table = (JTable)e.getSource();
int rowNum = Integer.valueOf(e.getActionCommand());
Object valueAt = table.getModel().getValueAt(rowNum, 0);
EditValuationsDialog edit = new EditValuationsDialog(null,true);
edit.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
edit.setTitle("Edit Valuations");
edit.setClientName(portfolioViewClientName.getText());
edit.setPortfolioType(portfolioViewInvestmentTypeCombo.getSelectedItem().toString());
edit.setPortfolioId(id);
edit.setOngoingValuationsId(Integer.parseInt(String.valueOf(valueAt)));
edit.setLocationRelativeTo(table);
edit.setVisible(true);
//CATCH THE CALL FROM EditValuationsDialog HERE!!!!//
}
};
EditValuationsDialog
//Action Listeners
private class AddBtnAction implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(someCondition)
{
return String / int to MainUIHolder (See where I want to catch it in MainUIHolder)
}
else
{
do nothing
}
}
}
In my code I have indicated from where the call to MainUIHolder should be generated and in what place I must catch that call in MainUIHolder. How can I do this call back work?
You could...
Add a static method to EditValuationsDialog that shows the dialog, evaluates the results and returns the value you are expecting...
public void actionPerformed(ActionEvent e)
{
int result = EditValuationsDialog.showDialog();
}
public class EditValuationsDialog ... {
//...
private int result = -1;
//...
public int getResult() {
return result;
}
//...
public static int showDialog(Component source, int rowNum, Object valueAt) {
EditValuationsDialog edit = null;
Window parent = SwingUtilities.windowFor(source);
if (parent instanceof Frame) {
edit = new EditValuationsDialog((Frame)parent,true);
} else if (parent instanceof Dialog) {
edit = new EditValuationsDialog((Dialog)parent,true);
} else {
edit = new EditValuationsDialog(null,true);
}
edit.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
edit.setTitle("Edit Valuations");
edit.setClientName(portfolioViewClientName.getText());
edit.setPortfolioType(portfolioViewInvestmentTypeCombo.getSelectedItem().toString());
edit.setPortfolioId(id);
edit.setOngoingValuationsId(Integer.parseInt(String.valueOf(valueAt)));
edit.setLocationRelativeTo(source);
edit.setVisible(true);
return edit.getResult();
}
//...
private class AddBtnAction implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(someCondition)
{
result = 0;
}
else
{
result = 1;
}
EditValuationsDialog.this.dispose();
}
}
}
Or you could...
Simply evaluate the results of getResult() from the above example directly...
Side note: Because I don't like extending from top level containers like JDialog, I tend to create some of my panels/components with static showDialog methods, thing something along the lines of a login panel for example. It means I could re-use the panel else where, but provides me with the convenience of been able to popup a dialog when I need to. I've also used JOptionPane from time to time to show these panels, but it depends on the complexity of the available actions...
Make the dialog modal (setModal(true)). Then the code after dialog.setVisible(true) is executed after the dialog is closed.
BTW it's better to pass the MainUIHolder JFrame instance as parent of the dialog.
You could add an interface to the EditValuationsDialog something like this:
Interface EditValuationsDialogInterface {
public void onAddClicked(addedVlue);
}
and then add it as such:
edit.setOnAddButtonCallback(new EditValuationsDialogInterface () {
#Override
onAddClicked(addedVlue){
//DO SOMETHING
}
});
in your EditValuationsDialog's add button onclick call add this:
onAddButtonClickedCallback.onAddClicked(retunrValue);
This allows you to have a direct link back to the original calling class.
How to return result from event handler in javafx? I have bellow code, and how to return data from event to function showPrompt? Is it possible to recover the data for the function of the event?
public static String showPrompt(String title, String defValue){
final Stage dlgStage = new Stage();
TextField txtPromptValue = new TextField(defValue);
Button btnOk = new Button("Ok");
Button btnCancel = new Button("Cancel");
btnOk.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent arg0) {
//How to return data from event to function?
dlgStage.close();
}
});
btnCancel.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent arg0) {
//How to return data from event to function?
dlgStage.close();
}
});
//
Label lblTitle = new Label(title);
lblTitle.setFont(Font.font("Amble CN", FontWeight.NORMAL, 14));
//
VBox vbox = new VBox(lblTitle,txtPromptValue,btnOk,btnCancel);
vbox.setAlignment(Pos.CENTER);
vbox.setMinSize(300, 200);
//
Scene dlgScene = new Scene(vbox);
//
dlgStage.setScene(dlgScene);
dlgStage.initStyle(StageStyle.UNDECORATED);
dlgStage.initModality(Modality.APPLICATION_MODAL);
dlgStage.setMinWidth(300);
dlgStage.setMinHeight(200);
dlgStage.show();
}
The short answer is you can't return a value.
Why ?
This code bellow is called a callback.
new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent arg0) {
dlgStage.close();
}
}
Callbacks have no return type, as you can see in the example above, it is void.
Callbacks are methods that you pass as an argument to another method. The other method will call you callback method when it wants. This means that callbacks are asynchronous. In your example, it calls the callback when you press the button.
In conclusion, you can't return from it using return.
What to do ?
You can call a method from your callback and sent your return value to it as an argument.
Example:
btnCancel.setOnAction(
new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent arg0) {
YourClass.setReturnValue("This is button Cancel");
dlgStage.close();
}
}
});
Where setReturnValue is a method belonging to YourClass or an instance of it so it will retail your returned value.
Another way better approach would be to create a class that extends Stage maybe. Also in your showPrompt method you will have to block execution using showAndWait() or similar.
In conclusion, you can't create your entire Prompt from just one method.
You can't, because by the time you've opened and closed the prompt stage, the main thread will have already passed the showPrompt method.
As Andrei said, what you need to do is create your own custom PromptStage with a showPrompt API that blocks the main thread until the prompt stage is closed.
public static String showPrompt(final String title, final String defValue)
{
// This line will block the main thread
// See the "showAndWait()" API from JavaFX
final boolean result = PromptStage.showPrompt("My Prompt Stage", " ");
// And when the stage is closed, it will carry on to this piece of code
if (result)
{
return "This is button OK";
}
else
{
return "This is button CANCEL";
}
}
Or you could even create instances of your PromptDialog if you like
public static String showPrompt(final String title, final String defValue)
{
final PromptStage pStage = new PromptStage();
// This line will block the main thread
// See the "showAndWait()" API from JavaFX
pStage.showAndWait();
return pStage.getResultAsString();
}
There are very many approaches here. To be honest, I won't bother writing the whole class for you. However, do comment if you're stuck.
Another option is to pass the showPrompt(...) method a StringProperty, and update the property in your OK button's handler. The caller of showPrompt can then create the StringProperty, register a listener with it, and observe it. Something like:
public String showPrompt(String title, String defValue, final StringProperty result){
// ...
final TextField txtPromptValue = new TextField(defValue);
// ...
btnOk.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
result.set(txtPromptValue.getText());
dlgStage.close();
}
});
// ...
}
Then you call the dialog with something like:
StringProperty dialogResult = new SimpleStringProperty();
dialogResult.addListener(new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> obs, String oldValue, String newValue) {
// process newValue, the value from the dialog...
}
});
showPrompt("Dialog Title", "Default value", dialogResult);
I want to create a popup (implemented as a DialogBox or other similar component) which i should be able to reuse in multiple pages or forms. I want the DialogBox to be able to return a value to the "opener".
I am thinking i.e. on a DialogBox that shows a table (obtained via RPC). That DialogBox can be used in several different pages. When the user selects a row, an object is "passed back to the page" (for example, calling a method on it), so it can write it to a form field, or do whatever with it. The called doesn't know anything about the logic inside de DialogBox, only knows how to deal with the returning type.
A good example of what i'm intending to do could be a DatePicker that returns a java.util.Date.
Have you done something similiar?
I appreciate your help.
Thanks!
David
It's really easy. You should first create an interface that will be implemented by all the pages opening you DialogBox :
public interface DialogBoxOpener {
void dialogBoxValidated (Date selectedDate);
void dialogBoxCancelled ();
}
Then, create your DialogBox, and take a DialogBoxOpener as parameter to your showDialogBox method :
public class MyDialogBox extends DialogBox {
private DialogBoxOpener opener = null;
private final Button cancelButton = new Button("Cancel");
private final Button validButton = new Button("Ok");
private final DateBox myDateBox = new DateBox();
public MyDialogBox () {
cancelButton.addClickHandler(new ClickHandler () {
#Override
public void onClick(final ClickEvent event) {
hide();
if (opener!=null)
opener.dialogBoxCancelled();
}
});
validButton.addClickHandler(new ClickHandler () {
#Override
public void onClick(final ClickEvent event) {
hide();
if (opener!=null)
opener.dialogBoxValidated(myDateBox.getValue());
}
});
// TODO : create your DialogBox
}
public void showDialogBox (final DialogBoxOpener opener) {
this.opener = opener;
// Show the DialogBox
center ();
}
}
And now, you can show your DialogBox from your page :
public class MyPage implements DialogBoxOpener {
private MyDialogBox myDialogBox = getMyDialogBox();
private void openDialogBox () {
myDialogBox.showDialogBox(this);
}
public void dialogBoxValidated (Date selectedDate) {
// TODO : Do something with the date
}
public void dialogBoxCancelled () {
// TODO : Do something
}
}