How to set action event for JavaFX Exception Dialog's button - java

I am now doing a simple sudoku game by JavaFX. And now I met some difficulty on dialog. I had create two scene, the menu scene contain only "new game" and "continue" button, main scene contain sudoku game. In the main scene I had created a check button to check if the gamer's answer is correct, where if it is incorrect then show a dialog like this img here and when it's correct,it might like this img here.
Then I found that CONFIRMATION ALERT is very similar. All I need to change is to the button's text and it's action,while when click retry to back to game scene and click quit to back to the main scene.
Now I know how to set action for the button in alert box, but i had some new question for that, I have no idea how to call the ventHandler<ActionEvent> in the statement.
Here is my code for two alert box
(source code from https://code.makery.ch/blog/javafx-dialogs-official/)
Alert right = new Alert(AlertType.CONFIRMATION);
right.setTitle("Checking Result");
right.setHeaderText(null);
right.setContentText("Your answer is correct. Would you like to start
again");
ButtonType restart = new ButtonType("Restart");
ButtonType quit = new ButtonType("Quit");
right.getButtonTypes().setAll(restart, quit);
Alert wrong = new Alert(AlertType.CONFIRMATION);
wrong.setTitle("Checking Result");
wrong.setHeaderText(null);
wrong.setContentText("Your answer is incorrect. Would you like to try
again");
ButtonType retry = new ButtonType("Retry");
wrong.getButtonTypes().setAll(retry, quit);
the code for actions
Optional<ButtonType> result = right.showAndWait();
if (result.isPresent() && result.get() == quit) {
stage.setScene(main_frame);
}else if(result.isPresent() && result.get() ==
restart) {// call the actionevent clears}
Optional<ButtonType> result = wrong.showAndWait();
if (result.isPresent() && result.get() == quit) {
stage.setScene(main_frame);
}else if(result.isPresent() && result.get() ==
retry) {// call the actionevent clears}
The code for eventhandler
final EventHandler<ActionEvent> clears = new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent event) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (digging_array[i][j] == 1) {
sudoku[i][j].setText(Integer.toString(final_Array[i][j]));
} else {
sudoku[i][j].setText("");
}
}
}
}
};

You did change the button type correctly regarding the right alert. Your last line does not change the buttons for the wrong alert. Replacing right with wrong will target the correct alert and thus change its buttons.
Checking which button was pressed can be done in multiple ways. Extract from the official documentation (https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/Alert.html):
Option 1: The 'traditional' approach
Optional<ButtonType> result = alert.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK) {
formatSystem();
}
Option 2: The traditional + Optional approach
alert.showAndWait().ifPresent(response -> {
if (response == ButtonType.OK) {
formatSystem();
}
});
Option 3: The fully lambda approach
alert.showAndWait()
.filter(response -> response == ButtonType.OK)
.ifPresent(response -> formatSystem());
instead of using ButtonType.OK you need to use your custom buttons.
EDIT
In your example you have to modify the code like this:
void clear() {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (digging_array[i][j] == 1) {
sudoku[i][j].setText(Integer.toString(final_Array[i][j]));
} else {
sudoku[i][j].setText("");
}
}
}
}
Optional<ButtonType> result = right.showAndWait();
if (result.isPresent() && result.get() == quit) {
stage.setScene(main_frame);
} else if(result.isPresent() && result.get() == restart) {
clear()
}
Optional<ButtonType> result = wrong.showAndWait();
if (result.isPresent() && result.get() == quit) {
stage.setScene(main_frame);
} else if(result.isPresent() && result.get() == retry) {
clear()
}

In the linked tutorial, there is an example on how to set custom actions (I shortened it a bit):
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog with Custom Actions");
ButtonType buttonTypeOne = new ButtonType("One");
ButtonType buttonTypeCancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeCancel);
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == buttonTypeOne){
// ... user chose "One"
} else {
// ... user chose CANCEL or closed the dialog
}
You can get the result (what the user clicked) via result.get() and check, which button was pressed (buttonTypeOne, buttonTypeCancel, ...).
When the user presses "One", you can now do something in the first body of the if statement.
In your code you are missing the showAndWait() call. If for example the user was right, you should do:
Observable<ButtonType> rightResult = right.showAndWait();
if (rightResult.isPresent()) {
if (rightResult.get() == restart) { //because "restart" is the variable name for your custom button type
// some action, method call, ...
} else { // In this case "quit"
}
}
Note, this is probably not the most elegant way (double if-statement) to do it. #Others feel free to edit my answer and put in a better way to do it.

Related

How to make the application to remember the boolean value?

Hi I am trying to add a sort of option window to my application using JOptionPane. If you click yes, a boolean is true. If you click no, a boolean is false and if you click the last button, I want another boolean to say false. And whenever that last button is clicked, I want that window to never be popped up again.
This is my code at the moment.
if (Configuration.LOGIN_MUSIC) {
Object[] options = {"Yes",
"No",
"No, don't ask me again!"};
int n = JOptionPane.showOptionDialog(null,
"Would you like to play the login music?",
"Login music",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[2]);
if (n == 0) {
Configuration.LOGIN_MUSIC = true;
} else if (n == 1){
Configuration.LOGIN_MUSIC = false;
} else {
Configuration.LOGIN_MUSIC_NEVER = true;
Configuration.LOGIN_MUSIC = false;
System.out.println("cancel");
System.out.println(Configuration.LOGIN_MUSIC_NEVER);
}
}
}
public static void main(String args[]) {
try {
if (Configuration.LOGIN_MUSIC_NEVER == false) {
checkForLoginMusic();
}
//System.out.println("The login music has been " + (Configuration.LOGIN_MUSIC ? "enabled" : "disabled"));
nodeID = 10;
portOff = 0;
setHighMem();
signlink.startpriv(InetAddress.getLocalHost());
clientSize = 0;
instance = new Client();
instance.createClientFrame(clientWidth, clientHeight);
//instance = new Jframe(args, clientWidth, clientHeight, false); uncomment if i want to add back the menu bar at top
} catch (Exception exception) {
}
}

How to listen for paste keyboard shortcut in SWT

I want to globally listen for CTRL + v in SWT. I can successfully listen for CTRL + c but paste seems to be handled differently in SWT. The following Listener shows how it works for copy and how it does not work for paste:
display.addFilter(SWT.KeyDown, new Listener() {
private final int CTRL = SWT.MOD1;
private boolean checkNextEventForPaste = false;
#Override
public void handleEvent(Event event) {
if(event.stateMask == CTRL && event.keyCode == 'c'){
System.out.println("copy: this works!");
}
else if(event.stateMask == CTRL && (event.keyCode == 'v'
|| event.keyCode == 'V'
|| event.keyCode == 0x16
|| event.keyCode == 118)){
System.out.println("paste: does not work!");
}
else if (event.keyCode == CTRL){
//control for paste is fired first
checkNextEventForPaste = true;
}
else if(checkNextEventForPaste){
if(event.keyCode == 65536){
System.out.println("custom solution: seems to not only apply for paste");
}
checkNextEventForPaste = false;
}
}
});
I debugged the paste case and created a custom solution. The paste keyboard short cut creates the following event sequence:
first event with stateMask = 0 and keyCode = CTRL
second event with stateMask = 0 and keyCode = 65536
The problem is that the custom paste solution seems to apply for other shortcuts too. For example the copy shortcut creates the following event sequence:
first event with stateMask = 0 and keyCode = CTRL
second event with stateMask = CTRL and keyCode = 'c'
third event with stateMask = 0 and keyCode = 65536
Why does SWT handle the paste shortcut in a different way? Is it possible that the paste shortcut is already consumed by an other control? Or does anybody know how I can identify the paste shortcut? A VerifyListener is not applicable in my use case. I have implemented a more or less complex UI with custom selection, as you can see here.
The code below works just fine for both Ctrl+c and Ctrl+v
public static void main(String[] args)
{
final Display d = new Display();
Shell s = new Shell(d);
d.addFilter(SWT.KeyDown, e ->
{
if (((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'c'))
{
System.out.println("copy");
}
else if (((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'v'))
{
System.out.println("paste");
}
});
s.pack();
s.open();
while (!s.isDisposed())
{
if (!d.readAndDispatch())
d.sleep();
}
d.dispose();
}

Cleaning code with too much conditionals

I've found this ugly piece of code from some time ago:
#FXML
private void buttSellAction(ActionEvent event){
InfoTip infoTip = new InfoTip();
if(comboPizza.getValue() != null){
if(comboPizzaSize.getValue() != null){
PizzaData selectedPizza = getPizzaData(comboPizza.getValue());
PizzaSizeData selectedPizzaSize = getPizzaSizeData(comboPizzaSize.getValue());
Date date = new Date();
Timestamp timestamp = new Timestamp(date.getTime());
if( selectedPizza != null ){
if(groupDelivery.getSelectedToggle().equals(radioNo)){ // sale without delivery
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirm");
alert.setHeaderText("Total cost: " + String.format("%.2f", selectedPizza.getPrice() + selectedPizzaSize.getPrice()));
alert.setContentText("Proceed with sale?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
insertSale(timestamp, currentUser.getLogin(), selectedPizza.getID(),
selectedPizzaSize.getSize(), false, selectedPizza.getPrice() + selectedPizzaSize.getPrice());
infoTip.getInfoTip().setId("greenInfoTip");
infoTip.showTip((Button)event.getSource(), " Saved ");
}
}else{ //Sale with delivery
String adress = textFAdress.getText();
String clientName = textFClientName.getText();
String telephone = textFTelephone.getText();
String deliveryCost = textFCost.getText();
boolean isAdressOK = ((adress.length() < 51) && (adress.isEmpty() == false))? true: false;
boolean isClientNameOK = (clientName.length() < 36)? true: false;
boolean isTelephoneOK = ((telephone.length() < 21) && (telephone.isEmpty() == false))? true: false;
boolean isCostOK;
try{ Double.valueOf(deliveryCost); isCostOK = true; }
catch(NumberFormatException exception){ isCostOK = false; }
if(isAdressOK == true){
if(isClientNameOK == true){
if(isTelephoneOK == true){
if(isCostOK == true){
double totalCost = selectedPizza.getPrice() + selectedPizzaSize.getPrice() + Double.valueOf(deliveryCost);
//everything is okey
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirm");
alert.setHeaderText("Total cost: " + totalCost);
alert.setContentText("Proceed with sale?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
int id = insertSale(timestamp, currentUser.getLogin(), selectedPizza.getID(),
selectedPizzaSize.getSize(), true, selectedPizza.getPrice() + selectedPizzaSize.getPrice());
insertDelivery(id, adress, clientName, telephone, Double.valueOf(deliveryCost));
infoTip.getInfoTip().setId("greenInfoTip");
infoTip.showTip((Button)event.getSource(), " Saved ");
} else {
// ... user chose CANCEL or closed the dialog
}
}else{ //cost not ok
infoTip.showTip(textFCost, "keep right format e.g. 4.35");
}
}else{ //telephone not ok
infoTip.showTip(textFTelephone, "max 20 characters, not empty");
}
}else{ //client name not ok
infoTip.showTip(textFClientName, "max 35 characters");
}
}else{ //adress not ok
infoTip.showTip(textFAdress, "max 50 characters, not empty");
}
}
}else{ //couldnt found selected pizza in pizzaList(which should not be possible)
ExceptionDialog exceptionDialog = new ExceptionDialog("Error when searching for selected pizza", new Exception());
exceptionDialog.showAndWait();
}
}else{ //pizza size not choosen
infoTip.showTip(comboPizzaSize, "select pizza size");
}
}else{ //pizza not choosen
infoTip.showTip(comboPizza, "select pizza");
}
}
I know now it has few major flaws:
method is doing too much and its too long,
it has too many conditional statements so it is easy to get lost,
unnecessary comments making code less readable.
repeated code,
possibly mixed levels of complexity.
testing it would be horrible.
something else??
How can I refactor it to make it clean and simple?
I'd take a slightly different approach to the other answer. I'd separate the validation from the other logic in the method. This means that you don't have to read lots of if statements to see the core logic of the method and if you want to change the validation you only need to update one statement in one place. eg for the first section, add a private method:
private PizzaSizeData getAndVerifySelectedPizza() {
if (comboPizza.getValue() == null) {
infoTip.showTip(comboPizza, "select pizza");
return null;
}
if (comboPizzaSize.getValue() == null) {
infoTip.showTip(comboPizzaSize, "select pizza size");
return null;
}
PizzaData selectedPizza = getPizzaData(comboPizza.getValue());
if (selectedPizza == null) {
ExceptionDialog exceptionDialog = new ExceptionDialog("Error when searching for selected pizza", new Exception());
exceptionDialog.showAndWait();
return null;
}
return getPizzaSizeData(comboPizzaSize.getValue());
}
You could return optionals instead of null but this illustrates the mechanism.
And then call the new method:
private void buttSellAction(ActionEvent event){
InfoTip infoTip = new InfoTip();
PizzaSizeData selectedPizzaSize = getAndVerifySelectedPizza();
if (selectedPizzaSize == null) {
return;
}
// Carry on with the method....
Putting validation at the start of a method with an early return statement is a common pattern so the multiple returns aren't going to confuse anyone and they allow each validation rule to be written separately.
you can use ladder if else in your code. like this
if(isAdressOK == true && isClientNameOK == true && isTelephoneOK == true && isCostOK == true){
double totalCost = selectedPizza.getPrice() + selectedPizzaSize.getPrice() + Double.valueOf(deliveryCost);
//everything is okey
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirm");
alert.setHeaderText("Total cost: " + totalCost);
alert.setContentText("Proceed with sale?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
int id = insertSale(timestamp, currentUser.getLogin(), selectedPizza.getID(),
selectedPizzaSize.getSize(), true, selectedPizza.getPrice() + selectedPizzaSize.getPrice());
insertDelivery(id, adress, clientName, telephone, Double.valueOf(deliveryCost));
infoTip.getInfoTip().setId("greenInfoTip");
infoTip.showTip((Button)event.getSource(), " Saved ");
} else {
// ... user chose CANCEL or closed the dialog
}
}else if(!isAdressOK == true){
infoTip.showTip(textFAdress, "max 50 characters, not empty");
}else if(!isClientNameOK == true){
infoTip.showTip(textFClientName, "max 35 characters");
}else if(!isTelephoneOK == true){
infoTip.showTip(textFTelephone, "max 20 characters, not empty");
}else{
infoTip.showTip(textFCost, "keep right format e.g. 4.35");
}
same for other if else condition.

StringUtils.isNumeric blocks JOptionPane cancel option

This is the code I'm using to process the input of an integer :
public static int getIntInput(String message) {
String numberStr = JOptionPane.showInputDialog(message);
while (!StringUtils.isNumeric(numberStr) || Integer.parseInt(numberStr) == 0) {
numberStr = JOptionPane.showInputDialog(message);
}
return Integer.parseInt(numberStr);
}
It works fine, except when I want to press "cancel" or close button on the JOption window. When I do that, the JOptionPane window shows up again.
How can I correctly close a JOptionPane window when the cancel or close button is pushed?
while (!StringUtils.isNumeric(numberStr) || Integer.parseInt(numberStr) == 0) {
if (numberStr == null) {
return 0;
}
else {
numberStr = JOptionPane.showInputDialog(message);
}
}
This does the trick.

javafx different actions on one button

I am making a blackjack game and was wondering if I could make it so that I had two different actions on a single button.
This is what I have so far on the hit button, but instead of having the second card show on a double click, I want it to show the second card when you press the button again.
public void hit(MouseEvent event) {
if (event.getClickCount() == 1) {
card5 = deck.dealCard();
pcard3.setImage(card5.getImage());
}
if (event.getClickCount() == 2) {
card6 = deck.dealCard();
pcard4.setImage(card6.getImage());
}
}
You can have an iterator whose value can be increased on each click. And for different values set different functionalities. See the code
int i =0;
public void hit(MouseEvent event) {
if (i%2== 0) {
card5 = deck.dealCard();
pcard3.setImage(card5.getImage());
} else if (i%2 == 1) {
card6 = deck.dealCard();
pcard4.setImage(card6.getImage());
}
i++;
}

Categories

Resources