I have an assignment where I have to create a functional calculator using JavaFX.
We need to create 3 text fields, one for the first num, one for the second num, and one for the result.
I'm almost done. but how do I make the program paste PI on the textfield being edited by the user? this is my code:
please help
public class Calculator extends Application{
double ans;
int t=6;
#Override
public void start(Stage primaryStage){
GridPane pane = new GridPane();
pane.setHgap(10);
pane.setVgap(10);
pane.setAlignment(Pos.CENTER);
pane.setPadding(new Insets(10, 10, 10, 10));
HBox hbox = new HBox();
TextField t1 = new TextField();
TextField t2 = new TextField();
TextField t3 = new TextField();
if(t1.getText().length() > 5){
t1.setText(t1.getText().substring(0, t));
t3.setText("Error! maximum 5 digits");
}
hbox.setAlignment(Pos.TOP_CENTER);
Button b1 = new Button("AC"); b1.setAlignment(Pos.CENTER); pane.add(b1,0,3);
Button b2 = new Button("PI");b2.setAlignment(Pos.CENTER); pane.add(b2,1,3);
Button b3 = new Button("Sqrt");b3.setAlignment(Pos.CENTER);pane.add(b3,2,3);
Button b4 = new Button("DEL");b4.setAlignment(Pos.CENTER);pane.add(b4,3,3);
Button b5 = new Button("7");b4.setAlignment(Pos.CENTER);pane.add(b5,0,4);
Button b6 = new Button("8");b6.setAlignment(Pos.CENTER);pane.add(b6,1,4);
Button b7 = new Button("9");b7.setAlignment(Pos.CENTER);pane.add(b7,2,4);
Button b8 = new Button("/");b8.setAlignment(Pos.CENTER);pane.add(b8,3,4);
Button b9 = new Button("4");b9.setAlignment(Pos.CENTER);pane.add(b9,0,5);
Button b10 = new Button("5");b10.setAlignment(Pos.CENTER);pane.add(b10,1,5);
Button b11 = new Button("6");b11.setAlignment(Pos.CENTER);pane.add(b11,2,5);
Button b12 = new Button("*");b12.setAlignment(Pos.CENTER);pane.add(b12,3,5);
Button b13 = new Button("1");b13.setAlignment(Pos.CENTER);pane.add(b13,0,6);
Button b14 = new Button("2");b14.setAlignment(Pos.CENTER);pane.add(b14,1,6);
Button b15 = new Button("3");b15.setAlignment(Pos.CENTER);pane.add(b15,2,6);
Button b16= new Button("-");b16.setAlignment(Pos.CENTER);pane.add(b16,3,6);
Button b17 = new Button("=");b17.setAlignment(Pos.CENTER);pane.add(b17,0,7);
Button b18 = new Button("0");b18.setAlignment(Pos.CENTER); pane.add(b18,1,7);
Button b19 = new Button(".");b19.setAlignment(Pos.CENTER);pane.add(b19,2,7);
Button b20 = new Button("+");b20.setAlignment(Pos.CENTER);pane.add(b20,3,7);
b1.setOnAction((ActionEvent e) -> {
t1.setText("");
t2.setText("");
t3.setText("");
});
b4.setOnAction((ActionEvent e) -> {
t3.setText("");
});
b3.setOnAction((ActionEvent e) -> {
ans=(Math.sqrt(Double.parseDouble(t1.getText())));
t3.setText(ans + "");
});
b20.setOnAction((ActionEvent e) -> {
ans=Double.parseDouble(t1.getText()) +Double.parseDouble(t2.getText());
t3.setText(ans + "");
});
b16.setOnAction((ActionEvent event) -> {
ans=Double.parseDouble(t1.getText()) - Double.parseDouble(t2.getText());
t3.setText(ans + "");
});
b12.setOnAction((ActionEvent event) -> {
ans=Double.parseDouble(t1.getText()) * Double.parseDouble(t2.getText());
t3.setText(ans + "");
});
b8.setOnAction((ActionEvent event) -> {
ans=Double.parseDouble(t1.getText()) /Double.parseDouble(t2.getText());
t3.setText(ans + "");
});
b2.setOnAction((ActionEvent event) -> {
ans= (Math.PI);
if(t1.isFocused()){
t1.setText(String.valueOf(ans));}
else if(t2.isFocused()){
t2.setText(String.valueOf(ans));
}
});
/*
t1.setOnMouseClicked((event) -> {
if(b2.isPressed()){
ans= (Math.PI);
t1.setText(String.valueOf(ans));}
});
t2.setOnMouseClicked((event) -> {
if(b2.isPressed()){
ans= (Math.PI);
t2.setText(String.valueOf(ans));}
});
*/
hbox.getChildren().addAll(t1,t2,t3);
BorderPane bp = new BorderPane();
bp.setTop(hbox);
bp.setCenter(pane);
Scene scene = new Scene(bp);
primaryStage.setTitle("Nora's Functional Calculator");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch (args);
}
}
Add something to track which field last had the focus. A field:
private TextField lastFocused;
And some listeners:
t1.focusedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean lost, Boolean gained) -> {
if (gained) {
lastFocused = t1;
}
});
t2.focusedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean lost, Boolean gained) -> {
if (gained) {
lastFocused = t2;
}
});
Then you can just set the text in the last focused field:
if (lastFocused != null) {
lastFocused.setText(String.valueOf(ans));
}
You might also want to initialize lastFocused to t1, and automatically toggle between t1 and t2 whenever you set the value. Make the text fields member variables and use a method to inject to the correct field:
private void setTextOfLastFocusedField(String val) {
if (lastFocused != null) {
lastFocused.setText(val);
}
if (lastFocused == t1) {
lastFocused = t2;
} else {
lastFocused = t1;
}
}
Related
I am trying to implement a Transition effect on a node, below is SSCE,
public class GridPaneExperiments extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Experiment");
Button button2 = new Button("Expand");
Button button3 = new Button("Button 3");
Button button4 = new Button("Button 4");
GridPane gridPane = new GridPane();
ToolBar bar = new ToolBar();
bar.getItems().addAll(button3, button4);
gridPane.add(button2, 0, 0, 1, 1);
gridPane.add(bar, 1, 0, 1, 1);
//Handle Mouse on Button
button2.setOnMouseEntered((MouseEvent event) -> {
TranslateTransition openNav = new TranslateTransition(new Duration(350), bar);
openNav.setToX(0);
if (bar.getTranslateX() != 0) {
openNav.play();
}
});
button2.setOnMouseExited((MouseEvent event) -> {
TranslateTransition closeNav = new TranslateTransition(new Duration(350), bar);
closeNav.setToX(-(((GridPane) gridPane).getWidth()));
closeNav.play();
});
//Handle Mouse on ToolBar
bar.setOnMouseExited((MouseEvent event) -> {
TranslateTransition closeNav = new TranslateTransition(new Duration(350), bar);
closeNav.setToX(-(((GridPane) gridPane).getWidth()));
closeNav.play();
});
bar.setOnMouseEntered((MouseEvent event) -> {
TranslateTransition openNav = new TranslateTransition(new Duration(350), bar);
openNav.setToX(0);
if (bar.getTranslateX() != 0) {
openNav.play();
}
});
Scene scene = new Scene(gridPane, 240, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
What I am trying to achieve is:
When mouse entered the Button "Expand" a Node will be opened to its right, after which if mouse entered the opened node it should not get close .
When mouse entered the Button "Expand" a Node will be opened and mouse exited from Button "Expand" (but not entered the opened node) , then the opened node should be closed.
Currently I am playing the Transition animation in Mouse events of both Button and Node.
How can I achieve this?
Just use a single animation for closing and opening. This way you can reverse the animation easily, don't run the risk of starting multiple animations in parallel and starting a closing animation is not an issue, since you change the animation to a opening animation when entering one of the nodes:
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Experiment");
Button button2 = new Button("Expand");
Button button3 = new Button("Button 3");
Button button4 = new Button("Button 4");
GridPane gridPane = new GridPane();
ToolBar bar = new ToolBar();
bar.getItems().addAll(button3, button4);
bar.setTranslateX(-10000); // somewhere outside view
gridPane.add(button2, 0, 0, 1, 1);
gridPane.add(bar, 1, 0, 1, 1);
TranslateTransition transition = new TranslateTransition(Duration.millis(300), bar);
transition.setToX(0);
EventHandler<MouseEvent> enterHandler = (MouseEvent event) -> {
transition.setFromX(-gridPane.getWidth());
Duration time = transition.getCurrentTime();
transition.setRate(1);
transition.playFrom(time);
};
EventHandler<MouseEvent> exitHandler = (MouseEvent event) -> {
if (!(button2.isHover() || bar.isHover())) {
Duration time = transition.getCurrentTime();
transition.setRate(-1);
transition.playFrom(time);
}
};
//Handle Mouse on Button
button2.setOnMouseEntered(enterHandler);
bar.setOnMouseEntered(enterHandler);
button2.setOnMouseExited(exitHandler);
bar.setOnMouseExited(exitHandler);
Scene scene = new Scene(gridPane, 240, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
you can simply achive your task using a helper HBox and setting setOnMouseExited to only that:
public class GridPaneExperiments extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Experiment");
Button button2 = new Button("Expand");
Button button3 = new Button("Button 3");
Button button4 = new Button("Button 4");
ToolBar bar = new ToolBar();
bar.getItems().addAll(button3, button4);
GridPane gridPane = new GridPane();
HBox hbox = new HBox(button2, bar);
hbox.setStyle("-fx-border-color: red");
gridPane.add(hbox, 0, 0);
//Handle Mouse on Button
button2.setOnMouseEntered((MouseEvent event) -> {
TranslateTransition openNav = new TranslateTransition(new Duration(350), bar);
openNav.setToX(0);
if (bar.getTranslateX() != 0) {
openNav.play();
}
});
// button2.setOnMouseExited((MouseEvent event) -> {
// TranslateTransition closeNav = new TranslateTransition(new Duration(350), bar);
// closeNav.setToX(-(((GridPane) gridPane).getWidth()));
// closeNav.play();
// });
//Handle Mouse on ToolBar
hbox.setOnMouseExited((MouseEvent event) -> {
TranslateTransition closeNav = new TranslateTransition(new Duration(350), bar);
closeNav.setToX(-(((GridPane) gridPane).getWidth()));
closeNav.play();
});
// bar.setOnMouseEntered((MouseEvent event) -> {
// TranslateTransition openNav = new TranslateTransition(new Duration(350), bar);
// openNav.setToX(0);
// if (bar.getTranslateX() != 0) {
// openNav.play();
// }
// });
Scene scene = new Scene(gridPane, 240, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
I am fairly new in JavaFX. I have a table with multiple columns and two buttons (btnBuilding , btnBSearch) outside the table. In the table, I have a column colAction where I want to have some buttons based on the button clicked outside the table. Suppose if I click btnBuilding I want to have 2 button Save and Refresh in my colAction column and Whenever I click btnBSearch I want to have 2 button Edit and Add in my colAction column. Inside the initialize() I tried like below
colAction.setCellFactory(col -> {
Button SaveButton = new Button("Save");
Button AddButton = new Button("Add");
Button RefreshButton = new Button("Refresh");
Button EditButton = new Button("Edit");
HBox hbox = new HBox(5);
if(btnBSearch.isFocused())
hbox.getChildren().addAll(AddButton,EditButton);
else if(btnBuilding.isFocused())
hbox.getChildren().addAll(SaveButton,RefreshButton);
TableCell<ModelBrBuilding, ModelBrBuilding> cell = new TableCell<ModelBrBuilding, ModelBrBuilding>() {
#Override
//Updating with the number of row
public void updateItem(ModelBrBuilding building, boolean empty) {
super.updateItem(building, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(hbox);
}
}
};
EditButton.setOnAction((ActionEvent event)->{
});
RefreshButton.setOnAction(event->{
});
SaveButton.setOnAction((ActionEvent event) -> {
});
AddButton.setOnAction(event -> {
});
return cell ;
});
But the problem is whatever button I click I am always getting Add and Edit in my action column. How can I add different button in my column based on the button (resides outside the table) I click?
The cellFactory runs only once for each cell. You need to make sure the cell is updated the button outside of the table is clicked.
You could do this by creating a property that contains a factory for the graphics and listen to it.
public interface GraphicFactory<T> {
Node createGraphic();
void updateGraphic(Node graphic, T item);
}
public class ReplacableGraphicTableCell<S, T> extends TableCell<S, T> {
private final ChangeListener<GraphicFactory<T>> factoryListener = (o, oldValue, newValue) -> {
if (newValue == null || isEmpty()) {
setGraphic(null);
} else {
Node n = newValue.createGraphic();
newValue.updateGraphic(n, getItem());
setGraphic(n);
}
};
private final ObservableValue<GraphicFactory<T>> factory;
private ReplacableGraphicTableCell(ObservableValue<GraphicFactory<T>> factory) {
this.factory = factory;
factory.addListener(factoryListener);
}
public static <E, F> Callback<TableColumn<E, F>, TableCell<E, F>> forTableColumn(ObservableValue<GraphicFactory<F>> factory) {
if (factory == null) {
throw new IllegalArgumentException();
}
return column -> new ReplacableGraphicTableCell(factory);
}
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
GraphicFactory<T> fact = factory.getValue();
if (fact == null) {
setGraphic(null);
} else {
Node graphic = getGraphic();
if (graphic == null) {
graphic = fact.createGraphic();
setGraphic(graphic);
}
fact.updateGraphic(graphic, item);
}
}
}
}
final ObjectProperty<GraphicFactory<Item>> graphicFactory = new SimpleObjectProperty<>();
TableColumn<Item, Item> column = new TableColumn<>();
column.setCellValueFactory(cd -> new SimpleObjectProperty<>(cd.getValue()));
column.setCellFactory(ReplacableGraphicTableCell.forTableColumn(graphicFactory));
ToggleGroup tg = new ToggleGroup();
tg.selectedToggleProperty().addListener((o, oldValue, newValue) -> {
GraphicFactory<Item> factory = null;
if (newValue != null) {
factory = (GraphicFactory<Item>) newValue.getUserData();
}
graphicFactory.set(factory);
});
RadioButton rb = new RadioButton("Add/Edit");
rb.setUserData(new GraphicFactory<Item>() {
#Override
public Node createGraphic() {
Button add = new Button("Add");
Button edit = new Button("Edit");
HBox hbox = new HBox(add, edit);
add.setOnAction(evt -> {
System.out.println("Add " + hbox.getUserData());
});
edit.setOnAction(evt -> {
System.out.println("Edit " + hbox.getUserData());
});
return hbox;
}
#Override
public void updateGraphic(Node graphic, Item item) {
graphic.setUserData(item);
}
});
rb.setToggleGroup(tg);
RadioButton rb2 = new RadioButton("Save/Refresh");
rb2.setUserData(new GraphicFactory<Item>() {
#Override
public Node createGraphic() {
Button save = new Button("Save");
Button refresh = new Button("Refresh");
HBox hbox = new HBox(save, refresh);
save.setOnAction(evt -> {
System.out.println("Save " + hbox.getUserData());
});
refresh.setOnAction(evt -> {
System.out.println("Refresh " + hbox.getUserData());
});
return hbox;
}
#Override
public void updateGraphic(Node graphic, Item item) {
graphic.setUserData(item);
}
});
rb2.setToggleGroup(tg);
It will not work this way. To begin with, you need to process the btnBuilding and btnBSearch buttons. Which of the buttons is pressed must reflect in the table you are using. For this purpose, one feature can be created propert to reflect which of the two buttons is pressed.
BooleanProperty showSearch = new SimpleBooleanProperty(false);
...
btnBuilding.setOnAction(e -> showSearch.setValue(false));
btnBSearch.setOnAction(e -> showSearch.setValue(true));
Then, you link the colAction column to the value of the property.
colAction.setCellValueFactory(cdf -> showSearch);
In this situation, you can create CellFactory to create the dynamic content cell
colAction.setCellFactory(col -> {
return new TableCell<String, Boolean>() {
Button SaveButton = new Button("Save");
Button AddButton = new Button("Add");
Button RefreshButton = new Button("Refresh");
Button EditButton = new Button("Edit");
HBox hboxBuilding = new HBox(5);
HBox hboxSearch = new HBox(5);
{
hboxBuilding.getChildren().addAll(AddButton,EditButton);
hboxSearch.getChildren().addAll(SaveButton,RefreshButton);
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
}
else {
setGraphic(item ? hboxBuilding : hboxSearch);
}
}
};
});
I'm a really new programmer so idk if this question sounds really stupid but..
This is my main:
package culminating;
import javafx.application.Application;
& all other necessary imports...
public class CulminatingMAIN extends Application {
//Set Global variables
int count = 0;
String name;
String gender = "Boy";
Label testLabel = new Label(gender + " has been selected");
#Override
public void start(Stage primaryStage) throws Exception {
/**
* ************************ SCENE 1 WORK *************************
*/
TextField nameTextField = new TextField();
nameTextField.setMaxWidth(100);
Label nameLabel = new Label("Please enter your name.");
Label genderLabel = new Label();
Label titleLabel = new Label("Math Adventure!");
titleLabel.setFont(Font.font("Arial", FontWeight.BOLD, 30));
Rectangle titleRectangle = new Rectangle();
titleRectangle.setFill(Color.TOMATO);
titleRectangle.setWidth(280);
titleRectangle.setHeight(60);
titleRectangle.setStroke(Color.BLACK);
titleRectangle.setStrokeWidth(2.0);
StackPane root = new StackPane(titleRectangle, titleLabel);
//Set VBox properties
VBox vbox1 = new VBox(25);
vbox1.setAlignment(Pos.TOP_CENTER);
vbox1.setPadding(new Insets(60, 0, 0, 0));
vbox1.setStyle("-fx-background-color: lightskyblue");
HBox genderBtnBox = new HBox(25);
genderBtnBox.setAlignment(Pos.CENTER);
//Set Scene 1 buttons
Button enterNameBtn = new Button("Enter");
Button goToScene2Btn = new Button("Continue");
//Set Radio Button functionality here
final ToggleGroup genderGroup = new ToggleGroup();
RadioButton rb1 = new RadioButton("Boy");
rb1.setToggleGroup(genderGroup);
rb1.setUserData("Boy");
rb1.setSelected(true);
RadioButton rb2 = new RadioButton("Girl");
rb2.setToggleGroup(genderGroup);
rb2.setUserData("Girl");
//Add panes, labels and buttons to the VBox
vbox1.getChildren().addAll(root, nameLabel, nameTextField, enterNameBtn, genderLabel, genderBtnBox);
Scene scene = new Scene(vbox1, 500, 500);
primaryStage.setScene(scene);
primaryStage.setTitle("Culminating Project");
primaryStage.show();
/**
* ************************ SCENE 2 WORK *************************
*/
//THIS IS ROUGH WORK SO FAR
//Here, testing out new scene to see that it loads properly (and it does)
Circle testCircle = new Circle();
testCircle.setRadius(30);
testCircle.setFill(Color.YELLOW);
StackPane testPane = new StackPane(testCircle, testLabel);
Scene scene2 = new Scene(testPane, 500, 500);
/**
* ************************ EVENTS *************************
*/
//Stores user-entered name and prompts for user gender. Adds Continue button
enterNameBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if ((count < 1) && (!nameTextField.getText().isEmpty())) {
name = nameTextField.getText();
genderLabel.setText("Hi " + name + "! Please select whether you are a boy or girl.");
genderBtnBox.getChildren().addAll(rb1, rb2);
vbox1.getChildren().add(goToScene2Btn);
count++;
}
}
});
//When pressed, changes the scene so that scene 2 is set instead
goToScene2Btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
primaryStage.setScene(scene2);
}
});
//Radio button selection is stored in gender variable
genderGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
#Override
public void changed(ObservableValue<? extends Toggle> ov,
Toggle old_toggle, Toggle new_toggle) {
if (genderGroup.getSelectedToggle() != null) {
gender = genderGroup.getSelectedToggle().getUserData().toString();
testLabel.setText(gender + " has been selected");
}
}
});
if (gender.equals("boy")){
{
}
}
else if (gender.equals("girl")){
{
}
}
}
public static void main(String[] args) {
launch(args);
}
}
Now I have another class called CharacterGraphic, which I want to call and make the graphic I created in it appear.
package culminating;
& all the other imports
public class CharacterGraphic extends Culminating_JavaFX {
public void start(Stage primaryStage) throws Exception {
String gender = "boy";
Pane pane = new Pane();
pane.setStyle("-fx-background-color: LIGHTBLUE");
pane.setPrefSize(200, 200);
Circle head = new Circle();
head.setRadius(50);
head.setCenterX(240);
head.setCenterY(120);
head.setFill(Color.BURLYWOOD);
etc etc (all other graphics i made)
How do I do this???? And where would I do this?? Any answers really, really appreciated!
How do I make my javaFX/8 dialog box shake more elegantly whenever a user input a wrong login name/password pair?.
Since the dialog in java8u40 does NOT have one, I set out to make one it myself. However, it doesn't look good enough.
What's wrong with it? Can someone help? Is there a better way in doing it?
public void loginDialog() {
// Create the custom dialog.
Dialog<Pair<String, String>> dialog = new Dialog<>();
dialog.setTitle("Mars Simulation Project");
dialog.setHeaderText("Log in");
dialog.setContentText("Enter your username and password : ");
dialog.initModality(Modality.NONE);
// Set the button types.
ButtonType loginButtonType = new ButtonType("Login", ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);
// Create the username and password labels and fields.
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 150, 10, 10));
TextField tfPlayer = new TextField();
tfPlayer.setPromptText("e.g. m03j");
PasswordField tfPassword = new PasswordField();
tfPassword.setPromptText("xxxx");
Button defaultPWB = new Button("Use Default");
Button guestB = new Button("As Guest");
defaultPWB.setOnAction(event -> {
tfPassword.setText("msp0");
} );
guestB.setOnAction(event -> {
tfPlayer.setText("Guest_");
tfPassword.setText("msp0");
} );
grid.add(new Label("Player Name :"), 0, 0);
grid.add(tfPlayer, 1, 0);
grid.add(guestB, 2, 0);
grid.add(new Label("Password :"), 0, 1);
grid.add(tfPassword, 1, 1);
grid.add(defaultPWB, 2, 1);
// Enable/Disable login button depending on whether a username was entered.
Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType);
loginButton.setDisable(true);
// Do some validation (using the Java 8 lambda syntax).
tfPlayer.textProperty().addListener((observable, oldValue, newValue) -> {
loginButton.setDisable(newValue.trim().isEmpty());
} );
dialog.getDialogPane().setContent(grid);
// Request focus on the player name field by default.
Platform.runLater(() -> tfPlayer.requestFocus());
// Convert the result to a player name /host address pair when the login
// button is clicked.
dialog.setResultConverter(dialogButton -> {
if (dialogButton == loginButtonType) {
return new Pair<>(tfPlayer.getText(), tfPassword.getText());
}
return null;
} );
Optional<Pair<String, String>> result = dialog.showAndWait();
result.ifPresent(input -> {
playerName = tfPlayer.getText();
logger.info("Player " + input.getKey() + " connecting to server at " + serverAddressStr);
try {
dialog.show();
makeContact(serverAddressStr);
// obtain a client id
boolean isSuccessful = sendRegister();
if (isSuccessful) {
dialog.close();
// establish chat...
} else {
// shake the dialog or send an alert to inform the user the
// player name is NOT valid
DialogEarthquakeCenter dec = new DialogEarthquakeCenter(dialog);
dec.startTimer();
try {
System.out.println("start sleeping ");
Thread.sleep(2000);
System.out.println("done sleeping ");
}
catch (InterruptedException e) {}
loginDialog();
}
} catch (Exception e) {
e.printStackTrace();
}
} );
So far, my problem is that as soon as I hit the button "login", the dialog will close by default.
Therefore I have to use dialog.show() to make it show up again.
[edit] This, however, still cannot prevent the momentary gap from happening (seeing the dialog disappear and reappear).
After that, I create an instance of DialogEarthquakeCenter in order to shake the dialog.
Note that my DialogEarthquakeCenter below is a direct modification of this original :
https://github.com/gigiigig/Java-Chat/blob/master/tag/FacebookChatCore_Original/src/facebookchat/ui/common/DialogEarthquakeCenter.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.scene.control.Dialog;
import javafx.util.Duration;
import javafx.util.Pair;
public class DialogEarthquakeCenter {
public static final int SHAKE_DISTANCE = 10;
public static final double SHAKE_CYCLE = 50;
public static final int SHAKE_DURATION = 500;
public static final int SHAKE_UPDATE = 5;
private Dialog<Pair<String, String>> dialog;
private int x, y;
private long startTime;
private Timer shakeTimer;
private final double TWO_PI = Math.PI * 2.0;
private Timeline timeline;
public DialogEarthquakeCenter(Dialog<Pair<String, String>> parent) {
dialog = parent;
}
/**
* Creates and starts the timer
*
* #return Scene
*/
public void startTimer() {
x = (int) dialog.getX();
y = (int) dialog.getY();
startTime = System.currentTimeMillis();
// Set up earth time text update
timeline = new Timeline(new KeyFrame(Duration.millis(SHAKE_DURATION), ae -> startNudging()));
//timeline.setCycleCount(javafx.animation.Animation.INDEFINITE);
timeline.play();
}
public void startNudging() {
x = (int) dialog.getX();
y = (int) dialog.getY();
startTime = System.currentTimeMillis();
shakeTimer = new Timer(SHAKE_UPDATE, new ActionListener() {
public void actionPerformed(ActionEvent e) {
shake();
}
});
shakeTimer.start();
}
public void shake() {
// calculate elapsed time
long elapsed = System.currentTimeMillis() - startTime;
//System.out.println("elapsed is " + elapsed);
// use sin to calculate an x-offset
double waveOffset = (elapsed % SHAKE_CYCLE) / SHAKE_CYCLE;
double angle = waveOffset * TWO_PI;
// offset the x-location by an amount
// proportional to the sine, up to shake_distance
int shakenX = (int) ((Math.sin(angle) * SHAKE_DISTANCE) + x);
Platform.runLater(() -> {
//dialog.hide();
dialog.setX(shakenX);
//System.out.println("set shakenX to " + shakenX);
dialog.setY(y);
dialog.show();
});
//try {Thread.sleep(20);}
//catch (InterruptedException ex) {}
// should we stop timer
if (elapsed >= SHAKE_DURATION) {
stopShake();
}
}
public void stopShake() {
shakeTimer.stop();
Platform.runLater(() -> {
timeline.stop();
dialog.close();
});
}
}
I did notice that controlsfx dialog has a shake() method.
Does anyone know if it works well ?
see https://code.google.com/p/mqtt-spy/source/browse/mqtt-spy/src/main/java/org/controlsfx/dialog/CustomDialogs.java?r=6ec0240e4e64d1b8cc2b59bc77cd5902a68e0c81
Thanks much for any comments!
There's a way you can add a transition once the user has click on the login button using the Dialog API, before the window is closed.
Using dialog.show() instead of dialog.showAndWait()`, the trick is just trapping the click action on the button, consume the event, and then perform the required logic.
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.show();
loginButton.addEventFilter(EventType.ROOT,
e->{
if(e.getEventType().equals(ActionEvent.ACTION)){
e.consume();
// (hardcoded) Login Validation
boolean isSuccessful = false;
if (isSuccessful) {
dialog.close();
}
else {
// perform animation and close the dialog (or any other action)
ShakeTransition anim = new ShakeTransition(dialog.getDialogPane(), t->dialog.close());
anim.playFromStart();
}
}
});
For the shake animation, I've modified ShakeTransition from Jasper Potts, in order to move the dialog window, as #jewelsea already pointed out:
/**
* Animate a shake effect on the given node
*
* Based on CachedTimelineTransition, a Transition that uses a Timeline internally
* and turns SPEED caching on for the animated node during the animation.
*
* https://github.com/fxexperience/code/blob/master/FXExperienceControls/src/com/fxexperience/javafx/animation/CachedTimelineTransition.java
*
* and ShakeTransition
*
* https://github.com/fxexperience/code/blob/master/FXExperienceControls/src/com/fxexperience/javafx/animation/ShakeTransition.java
*
* #author Jasper Potts
*/
class ShakeTransition extends Transition {
private final Interpolator WEB_EASE = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
private final Timeline timeline;
private final Node node;
private boolean oldCache = false;
private CacheHint oldCacheHint = CacheHint.DEFAULT;
private final boolean useCache=true;
private final double xIni;
private final DoubleProperty x = new SimpleDoubleProperty();
/**
* Create new ShakeTransition
*
* #param node The node to affect
*/
public ShakeTransition(final Node node, EventHandler<ActionEvent> event) {
this.node=node;
statusProperty().addListener((ov, t, newStatus) -> {
switch(newStatus) {
case RUNNING:
starting();
break;
default:
stopping();
break;
}
});
this.timeline= new Timeline(
new KeyFrame(Duration.millis(0), new KeyValue(x, 0, WEB_EASE)),
new KeyFrame(Duration.millis(100), new KeyValue(x, -10, WEB_EASE)),
new KeyFrame(Duration.millis(200), new KeyValue(x, 10, WEB_EASE)),
new KeyFrame(Duration.millis(300), new KeyValue(x, -10, WEB_EASE)),
new KeyFrame(Duration.millis(400), new KeyValue(x, 10, WEB_EASE)),
new KeyFrame(Duration.millis(500), new KeyValue(x, -10, WEB_EASE)),
new KeyFrame(Duration.millis(600), new KeyValue(x, 10, WEB_EASE)),
new KeyFrame(Duration.millis(700), new KeyValue(x, -10, WEB_EASE)),
new KeyFrame(Duration.millis(800), new KeyValue(x, 10, WEB_EASE)),
new KeyFrame(Duration.millis(900), new KeyValue(x, -10, WEB_EASE)),
new KeyFrame(Duration.millis(1000), new KeyValue(x, 0, WEB_EASE))
);
xIni=node.getScene().getWindow().getX();
x.addListener((ob,n,n1)->(node.getScene().getWindow()).setX(xIni+n1.doubleValue()));
setCycleDuration(Duration.seconds(1));
setDelay(Duration.seconds(0.2));
setOnFinished(event);
}
/**
* Called when the animation is starting
*/
protected final void starting() {
if (useCache) {
oldCache = node.isCache();
oldCacheHint = node.getCacheHint();
node.setCache(true);
node.setCacheHint(CacheHint.SPEED);
}
}
/**
* Called when the animation is stopping
*/
protected final void stopping() {
if (useCache) {
node.setCache(oldCache);
node.setCacheHint(oldCacheHint);
}
}
#Override
protected void interpolate(double d) {
timeline.playFrom(Duration.seconds(d));
timeline.stop();
}
}
And this will be a JavaFX application using your login dialog:
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Show Login Dialog");
btn.setOnAction(mevent -> {
// Create the custom dialog.
Dialog<Pair<String, String>> dialog = new Dialog<>();
dialog.setTitle("Mars Simulation Project");
dialog.setHeaderText("Log in");
dialog.setContentText("Enter your username and password : ");
dialog.initModality(Modality.NONE);
// Set the button types.
ButtonType loginButtonType = new ButtonType("Login", ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);
// Create the username and password labels and fields.
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 150, 10, 10));
TextField tfPlayer = new TextField();
tfPlayer.setPromptText("e.g. m03j");
PasswordField tfPassword = new PasswordField();
tfPassword.setPromptText("xxxx");
Button defaultPWB = new Button("Use Default");
Button guestB = new Button("As Guest");
defaultPWB.setOnAction(event -> {
tfPassword.setText("msp0");
} );
guestB.setOnAction(event -> {
tfPlayer.setText("Guest_");
tfPassword.setText("msp0");
} );
grid.add(new Label("Player Name :"), 0, 0);
grid.add(tfPlayer, 1, 0);
grid.add(guestB, 2, 0);
grid.add(new Label("Password :"), 0, 1);
grid.add(tfPassword, 1, 1);
grid.add(defaultPWB, 2, 1);
// Enable/Disable login button depending on whether a username was entered.
Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType);
loginButton.setDisable(true);
// Do some validation (using the Java 8 lambda syntax).
tfPlayer.textProperty().addListener((observable, oldValue, newValue) -> {
loginButton.setDisable(newValue.trim().isEmpty());
} );
dialog.getDialogPane().setContent(grid);
// Request focus on the player name field by default.
Platform.runLater(() -> tfPlayer.requestFocus());
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.show();
loginButton.addEventFilter(EventType.ROOT,
e->{
if(e.getEventType().equals(ActionEvent.ACTION)){
e.consume();
// (hardcoded) Login Validation
boolean isSuccessful = false;
if (isSuccessful) {
dialog.close();
}
else {
ShakeTransition anim = new ShakeTransition(dialog.getDialogPane(), t->dialog.close());
anim.playFromStart();
}
}
});
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Shaky Login Dialog");
primaryStage.setScene(scene);
primaryStage.show();
}
I am trying to develop a wizard using the new ControlsFX 8.20.7 release. I have taken a look at the following example: BitBucket ControlsFX, and especially the method
showLinearWizard()
I simply can't understand how to use this API, can anyone help me get going or link to some examples?
This is my code right now, full of errors:
public class WizardTest extends Application {
private final ComboBox<StageStyle> styleCombobox = new ComboBox<>();
private final ComboBox<Modality> modalityCombobox = new ComboBox<>();
private final CheckBox cbUseBlocking = new CheckBox();
private final CheckBox cbCloseDialogAutomatically = new CheckBox();
private final CheckBox cbShowMasthead = new CheckBox();
private final CheckBox cbSetOwner = new CheckBox();
private final CheckBox cbCustomGraphic = new CheckBox();
private Stage stage;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
showLinearWizard();
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void showLinearWizard() {
// define pages to show
Wizard wizard = new Wizard();
wizard.setTitle("Linear Wizard");
// --- page 1
int row = 0;
GridPane page1Grid = new GridPane();
page1Grid.setVgap(10);
page1Grid.setHgap(10);
page1Grid.add(new Label("First Name:"), 0, row);
TextField txFirstName = createTextField("firstName");
wizard.getValidationSupport().registerValidator(txFirstName, Validator.createEmptyValidator("First Name is mandatory"));
page1Grid.add(txFirstName, 1, row++);
page1Grid.add(new Label("Last Name:"), 0, row);
TextField txLastName = createTextField("lastName");
wizard.getValidationSupport().registerValidator(txLastName, Validator.createEmptyValidator("Last Name is mandatory"));
page1Grid.add(txLastName, 1, row);
WizardPane page1 = new WizardPane();
page1.setHeaderText("Please Enter Your Details");
page1.setContent(page1Grid);
// --- page 2
final WizardPane page2 = new WizardPane() {
#Override
public void onEnteringPage(Wizard wizard) {
String firstName = (String) wizard.getSettings().get("firstName");
String lastName = (String) wizard.getSettings().get("lastName");
setContentText("Welcome, " + firstName + " " + lastName + "! Let's add some newlines!\n\n\n\n\n\n\nHello World!");
}
};
page2.setHeaderText("Thanks For Your Details!");
// --- page 3
WizardPane page3 = new WizardPane();
page3.setHeaderText("Goodbye!");
page3.setContentText("Page 3, with extra 'help' button!");
ButtonType helpDialogButton = new ButtonType("Help", ButtonData.HELP_2);
page3.getButtonTypes().add(helpDialogButton);
Button helpButton = (Button) page3.lookupButton(helpDialogButton);
helpButton.addEventFilter(ActionEvent.ACTION, actionEvent -> {
actionEvent.consume(); // stop hello.dialog from closing
System.out.println("Help clicked!");
});
// create wizard
wizard.setFlow(new LinearFlow(page1, page2, page3));
System.out.println("page1: " + page1);
System.out.println("page2: " + page2);
System.out.println("page3: " + page3);
// show wizard and wait for response
wizard.showAndWait().ifPresent(result -> {
if (result == ButtonType.FINISH) {
System.out.println("Wizard finished, settings: " + wizard.getSettings());
}
});
}
private TextField createTextField(String id) {
TextField textField = new TextField();
textField.setId(id);
GridPane.setHgrow(textField, Priority.ALWAYS);
return textField;
}
}
The problem was that I forgot to add the
openjfx-dialogs.jar