Unable to align text and button in dialogbox in javafx - java

Following is my code for showing a dialog when some error or warning occurs,
but I am unable to align the text and button. both things cover each other.
//INITIALIZING SCENE FOR dialogStage
StackPane dialogStackPane = new StackPane();
dialogStackPane.setAlignment(Pos.CENTER);
dialogStackPane.setLayoutX(0);
dialogStackPane.setLayoutY(0);
dialogStackPane.setPrefWidth(250.0);
dialogStackPane.setPrefHeight(150.0);
// dialogStage height and width
dialogStage.setHeight(150.0);
dialogStage.setWidth(250.0);
//Scene for dialogStage
sceneDialog = new Scene(dialogStackPane, 150.0, 250.0);
sceneDialog.getStylesheets().add("/styles/Login.css");
// Dialog box button
btnDialog = new Button("close");
btnDialog.setMinSize(10.0, 20.0);
ObservableList ol = dialogStackPane.getChildren();
ol.add(textWarning);
ol.add(btnDialog);
textWarning.setX(0);
textWarning.setY(0);
btnDialog.setLayoutX(0);
btnDialog.setLayoutY(0);
// SETTING THE MODALITY OF THE DIALOG BOX
dialogStage.initModality(Modality.APPLICATION_MODAL);
dialogStage.setTitle("Warning");
dialogStage.setScene(sceneDialog);
// Setting event listener on warning dialogStage button b
btnDialog.addEventFilter(MouseEvent.ANY, new EventHandler<MouseEvent>() {
// This handles the Mouse event of the dialog box button.
#Override
public void handle(MouseEvent t) {
if (t.getEventType() == MouseEvent.MOUSE_CLICKED) {
dialogStage.close();
}
}
});
textWarning.setLayoutX(20);
textWarning.setLayoutY(200);
// textWarning.setTextOrigin(VPos.TOP);
// textWarning.setTextAlignment(TextAlignment.LEFT);
// Sets the warning box
dialogStage.setResizable(false);
any help would be appreciated.

Use an AnchorPane instead of a StackPane. Then you can position any node by static methods in AnchorPane class.
AnchorPane pane = new AnchorPane();
...
AnchorPane.setLeftAnchor(textWarning, 50.0);
AnchorPane.setLeftAnchor(btnDialog, 50.0);
AnchorPane.setTopAnchor(textWarning, 20.0);
AnchorPane.setTopAnchor(btnDialog, 50.0);
...
pane.getChildren().addAll(textWarning, btnDialog);
...
sceneDialog = new Scene(pane, 150.0, 250.0);
...

Related

How to set an action to occur on pressing of any keyboard key in JavaFX?

So I want to do a title Menu for a video game project for college. I want to display a message press any key to continue... then when the user pressed any key, including the mouse, a method would run to set the stage to the next menu.
I posted all the relevant code bellow but short version is:
BackgroundImangeDisplayPane extends display pane and adds a background image.
TitleCard extends BackgroundImangeDisplayPane adds a VBox and 2 labels to the VBox
I'm using public void start(Stage primaryStage) throws Exception as the main, I set the setOnActionxxx methods here
I have tried using the set on action method on root and Vbox and non of them work... when I click nothing happens... But when I resize the window The root.setOnActionXXX "activates".
If I write the setOnAction methods on the TitleCard class It kind of works but then I cant switch the stage.
I will post the code bellow as well an explanation of the Scene structure its not to complicated:
// this will be the borderpane for every scene it recives a backgund
//images that will be present in every menu
public BackgroundImangeDisplayPane() {
try {
stream = new FileInputStream(imagePath.toString());
Image image = new Image(stream);
ImageView imageView = new ImageView();
imageView.setImage(image);
imageView.setFitWidth(1920);
imageView.setPreserveRatio(true);
this.getChildren().add(imageView);
BackgroundSize backgoundSize = new BackgroundSize(AUTO, AUTO, true, true, true, true);
BackgroundImage backgroundImage = new BackgroundImage(image, NO_REPEAT, NO_REPEAT, CENTER, backgoundSize);
Background background = new Background(backgroundImage);
this.setBackground(background);
} catch (Exception e) {
}
}
//This extends `BackgroundImangeDisplayPane` and places on top of it a A Vbox with two lables: the title and "press any key to continue..."
// it then adds styles to the labels
public class TitleCard extends BackgroundImangeDisplayPane {
Label title = new Label("Boats & Docks"); // lable 1
Label subtitle = new Label("Press any key to continue ..."); label2
public TitleCard(){
super();
VBox vbox = new VBox();
vbox.getChildren().add(title);
vbox.getChildren().add(subtitle);
this.setCenter(vbox);
this.setAlignment(vbox, Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
title.setFont(new Font(170)); // set to Label
title.setTextFill(Color.SNOW);
title.setEffect(new DropShadow());
subtitle.setFont( new Font (30));
}
}
...
//Works as the "main" in javaFX
private Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
TitleCard root = new TitleCard();
/*BasicMenu menu = new BasicMenu(5);
menu.ButtonSetOnAction(0, e -> changeScene() );
BackgroundImangeWithCustomMenu background = new
BackgroundImangeWithCustomMenu(menu,50,50);
root.setCenter(background);*/
Button b = new Button();
b.setOnAction(e -> changeSceneToLoginMenu());
System.out.println(root.getChildren().get(1).getClass());
root.getChildren().get(1).setFocusTraversable(true);
root.getChildren().get(1).setOnMouseClicked(e -> changeSceneToLoginMenu());
root.getChildren().get(1).setOnKeyPressed(e -> changeSceneToLoginMenu());
root.getChildren().get(0).setOnMouseClicked(e -> changeSceneToLoginMenu());
root.getChildren().get(0).setOnKeyPressed(e -> changeSceneToLoginMenu());
/*
root.setOnMouseClicked(e -> changeSceneToLoginMenu());
root.setOnKeyReleased(e -> changeSceneToLoginMenu());
root.setOnKeyPressed(e -> changeSceneToLoginMenu());
*/
Scene scene = new Scene(root, 1280, 720);
primaryStage.setScene(scene);
primaryStage.show();
this.primaryStage = primaryStage;
}
As proposed by James in comments, when a key is pressed on the scene, navigate to the next scene (or replace the root in the current scene, and remove the key press handler).
scene.setOnKeyPressed(e -> navigateToNextScene());
I managed to find a very simple working solution but I don't fully undestand why it does work. I Noticed that if I set the handler in the same class the node was instanciated the handler would work fine But if I tried to get the node with a method to the main fuction via root.getChildren().get(1) and then cast it to the VBox element the handler would not work.
As a solution I made the VBox a field and wrote a setter method for the VBox event Handler in the TitleCard Class. This fixed the problem.
I marked the code added as solution code with comments
public class TitleCard extends BackgroundImangeDisplayPane {
Label title = new Label("Boats & Docks"); // lable 1
Label subtitle = new Label("Press any key to continue ..."); label2
VBox vbox = new VBox; // solution code
public TitleCard(){
super();
VBox vbox = new VBox();
vbox.getChildren().add(title);
vbox.getChildren().add(subtitle);
this.setCenter(vbox);
this.setAlignment(vbox, Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
title.setFont(new Font(170)); // set to Label
title.setTextFill(Color.SNOW);
title.setEffect(new DropShadow());
subtitle.setFont( new Font (30));
}
// Solution Code
public void setVBoxHandler(EventHandler<? super MouseEvent> value){
vbox.setOnMouseClicked(value);
}
}
Then I set the handler in the start method:
public void start(Stage primaryStage) throws Exception {
TitleCard root = new TitleCard();
VBox vBox =(VBox) root.getChildren().get(1);
root.setVBoxHandler(e->changeSceneToLoginMenu() ); // solution Code
Scene scene = new Scene(root, 1280, 720);
primaryStage.setScene(scene);
primaryStage.show();
this.primaryStage = primaryStage;
}
public void changeSceneToLoginMenu() {
System.out.println("It finally worked");
Scene currentScene = new Scene(new Group(),100,100); // just a demo
primaryStage.setScene(currentScene);
}
notes: The type of value on the method setVBoxHandler(EventHandler<? super MouseEvent> value) will depend on the setOnXXX method used. For example I tested and this soultion also works for buttons just need to change the type to EventHandler<ActionEvent> value.
Some coments on the question posted links on "how to use handlers", this posts used anomimous classes. I belived This way is outdated. I used lambdas in the code the end result is the same but more redable code
Just for reference if future readers are using anomimous classes the solution would be the same just change the way you set up the handler:
// lambdas
setVBoxHandler( e -> System.out.println("Code run if mouse is clicked "));
// anomimous classes
setVBoxHandler(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event){
System.out.println("Code run if mouse is clicked ");
}
});

JavaFX hover property

I have a GridPane which is populated by rectangles. What I want to do is to display a new pane while hovering over a particular rectangle(member of the grid pane). Lets have an example with this VBox code example below. How could I make it display on hover ?
Rectangle r = new Rectangle(RECTANGLE_SIZE, RECTANGLE_SIZE);
r.hoverProperty().addListener((observable) -> {
r.setFill(Color.BLACK);
VBox box = new VBox();
Button x = new Button("Test");
box.getChildren().add(x);
});
the set fill works properly
In your example you need to specify Node which would be parent of VBox. For now you are always creating new VBox, but never add it to current scene graph. Try this:
r.hoverProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean show) -> {
if (show) {
VBox box = new VBox();
Button x = new Button("Test");
box.getChildren().add(x);
parent.getChildren().add(box);
} else {
parent.getChildren().clear();
}
});

JavaFX: Node doesn't recognize when mouse is being dragged over it

I'm attempting to have a node recognize when a drag gesture is performed from one node to another.
I've tried to set a MouseDragEvent through node#setOnMouseDragOver but it doesn't ever get invoked.
This is my implementation:
nfaNode.setOnMouseDragOver(event1 -> {
System.out.println("over " + nfaNode.getText().getText());
});
I want it to know that Q_1 is being dragged over so that I am able to tell that Q_0 is being dragged to Q_1.
During a dragging gesture events are only delivered to the node where the gesture started by default. To change this, you need to call startFullDrag in the onDragDetected handler. Also if you move the node around, setting mousetransparent to true may be necessary for the mouse events not to be delivered just to the dragged node.
Example
#Override
public void start(Stage primaryStage) {
Rectangle rect1 = new Rectangle(100, 100, Color.BLUE);
Rectangle rect2 = new Rectangle(200, 200, 100, 100);
rect2.setFill(Color.RED);
rect1.setOnDragDetected(evt -> {
rect1.startFullDrag();
});
rect2.setOnMouseDragOver(evt -> {
System.out.println("over");
});
Pane root = new Pane(rect1, rect2);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
Comment out rect1.startFullDrag(); and you'll not see any output in the console.
Update
You can retrieve the gestureSource from a MouseDragEvent to get the node where you started the drag gesture and use getSource to retrieve the node where the event handler was added. Example:
#Override
public void start(Stage primaryStage) {
TextField text1 = new TextField();
TextField text2 = new TextField();
EventHandler<MouseEvent> dragDetected = evt -> ((Node) evt.getSource()).startFullDrag();
EventHandler<MouseDragEvent> dragOver = evt -> {
System.out.println("over " + ((TextField) evt.getGestureSource()).getText());
};
EventHandler<MouseDragEvent> dragReleased = evt -> {
TextField target = (TextField) evt.getSource();
TextField source = (TextField) evt.getGestureSource();
if (source != target) {
target.setText(source.getText());
source.clear();
}
};
for (TextField tf : Arrays.asList(text1, text2)) {
tf.setOnDragDetected(dragDetected);
tf.setOnMouseDragOver(dragOver);
tf.setOnMouseDragReleased(dragReleased);
}
VBox root = new VBox(text1, text2);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

How to collapse TitledPane programmatically in JavaFX?

Does anyone know how to programmatically collapse a TitledPane in JavaFX? I know the methods setExpanded and setCollapsible are available but it only enables the property. I want to collapse the pane without any mouse interaction.
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
TitledPane titledPane = new TitledPane();
titledPane.setAnimated(true);
titledPane.setText("This is a test");
titledPane.setContent(btn);
titledPane.setExpanded(true);
Accordion accordion = new Accordion();
accordion.getPanes().add(titledPane);
Button show = new Button("Expand");
show.setOnAction((ActionEvent event) -> {
accordion.setExpandedPane(titledPane); // Expanded Pane works!
});
Button hide = new Button("Collapse");
hide.setOnAction((ActionEvent event) -> {
//???
});
HBox layout = new HBox(10);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
layout.getChildren().addAll(show, hide, accordion);
Scene scene = new Scene(layout, 320, 240);
scene.setFill(Color.GHOSTWHITE);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
Here is the result I am looking for
Since only one TitledPane in the same Accordion can be expanded at any time, the Accordion (to some extent) manages the expanded state of any TitledPanes it contains.
You can do
accordion.setExpandedPane(titledPane);
to get the accordion to expand titledPane.
Calling titledPane.setExpanded(true); also works, but only if you invoke this after the window has been shown (for reasons I don't fully understand).

JavaFX - BorderPane/StackPane not resizing after children changed

I am having a resize issue when content is added and removed from a JavaFX BorderPane. The BorderPane content is not being resized until the window is manually resized. I have written a small test app to model this behavior. The application builds a BorderPane that contains a rectangle embedded within a StackPane at the center of the BorderPane. Along the bottom of the BorderPane there is a VBox and HBox that contain text and a separator. There is a menu item that removes the content from the bottom of the BorderPane (MoveText -> Right) and adds similar content to the right position of the BorderPane.
When the text is added to the right position of the BorderPane, the rectangle overlaps the text. In otherwords the content of the BorderPane center is overlapping the content in the BorderPane right.
I saw the following link -
https://stackoverflow.com/questions/5302197/javafx-bug-is-there-a-way-to-force-repaint-this-is-not-a-single-threading-pr
Calling requestLayout does not seem to help. I have also tried calling impl_updatePG and impl_transformsChanged on various nodes in the graph. I got this idea from this thread - https://forums.oracle.com/forums/thread.jspa?threadID=2242083
public class BorderPaneExample extends Application
{
private BorderPane root;
private StackPane centerPane;
#Override
public void start(Stage primaryStage) throws Exception
{
root = new BorderPane();
root.setTop(getMenu());
root.setBottom(getBottomVBox());
centerPane = getCenterPane();
root.setCenter(centerPane);
Scene scene = new Scene(root, 900, 500);
primaryStage.setTitle("BorderPane Example");
primaryStage.setScene(scene);
primaryStage.show();
}
private MenuBar getMenu()
{
MenuBar menuBar = new MenuBar();
MenuItem rightMenuItem = new MenuItem("Right");
rightMenuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
root.setRight(getRightHBox());
root.setBottom(null);
root.requestLayout();
}
});
MenuItem bottomMenuItem = new MenuItem("Bottom");
bottomMenuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
root.setRight(null);
root.setBottom(getBottomVBox());
}
});
Menu menu = new Menu("Move text");
menu.getItems().add(rightMenuItem);
menu.getItems().add(bottomMenuItem);
menuBar.getMenus().addAll(menu);
return menuBar;
}
private HBox getRightHBox()
{
HBox hbox = new HBox();
VBox vbox = new VBox(50);
vbox.setPadding(new Insets(0, 20, 0, 20));
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(new Text("Additional Info 1"),
new Text("Additional Info 2"), new Text("Additional Info 3"));
hbox.getChildren().addAll(new Separator(Orientation.VERTICAL), vbox);
return hbox;
}
private VBox getBottomVBox()
{
VBox vbox = new VBox();
HBox hbox = new HBox(20);
hbox.setPadding(new Insets(5));
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().addAll(new Text("Footer Item 1")
, new Text("Footer Item 2"), new Text("Footer Item 3"));
vbox.getChildren().addAll(new Separator(), hbox);
return vbox;
}
private StackPane getCenterPane()
{
StackPane stackPane = new StackPane();
stackPane.setAlignment(Pos.CENTER);
final Rectangle rec = new Rectangle(200, 200);
rec.setFill(Color.DODGERBLUE);
rec.widthProperty().bind(stackPane.widthProperty().subtract(50));
rec.heightProperty().bind(stackPane.heightProperty().subtract(50));
stackPane.getChildren().addAll(rec);
return stackPane;
}
public static void main(String[] args)
{
Application.launch(args);
}
}
Any suggestions would be appreciated.
Thanks
For the sake of having an answer:
The StackPane needs to have its minimum size set in order to do clipping. So if you explicitly add stackPane.setMinSize(0, 0); to the getCenterPane() method, it should fix your problem.
So your getCenterPane() method would now look like this:
private StackPane getCenterPane()
{
StackPane stackPane = new StackPane();
stackPane.setMinSize(0, 0);
stackPane.setAlignment(Pos.CENTER);
final Rectangle rec = new Rectangle(200, 200);
rec.setFill(Color.DODGERBLUE);
rec.widthProperty().bind(stackPane.widthProperty().subtract(50));
rec.heightProperty().bind(stackPane.heightProperty().subtract(50));
stackPane.getChildren().addAll(rec);
return stackPane;
}

Categories