How to position a button in JavaFX scene ? ( Randomly ) - java

I want somehow to make the button change his position randomly when i push the button. I've got one idea how to solve this problem, one of them i've highlighted below, but i already think that this is not what i need.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Ulesanne6 extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Test");
Button btn = new Button();
btn.setText("Hello");
btn.setOnAction(new EventHandler <javafx.event.ActionEvent>() {
#Override
public void handle(javafx.event.ActionEvent event) {
//btn.setLayoutY(Math.random());
//btn.setLayoutX(Math.random());
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.show();
}
}
I think that somewhere here i suppose to do that, but i don't know how... yet.
public void handle(javafx.event.ActionEvent event) {
//btn.setLayoutY(Math.random());
//btn.setLayoutX(Math.random());
}

How about this?
public void handle(javafx.event.ActionEvent event) {
btn.setTranslateX(ThreadLocalRandom.current().nextDouble(800));
btn.setTranslateY(ThreadLocalRandom.current().nextDouble(600));
}
You might also want to make sure that the button is placed somewhere on the screen and not off the screen. So if you know the width and height of the button, take it into account.
public void handle(javafx.event.ActionEvent event) {
btn.setTranslateX(ThreadLocalRandom.current().nextDouble(800 - buttonWidth));
btn.setTranslateY(ThreadLocalRandom.current().nextDouble(600 - buttonHeight));
}
EDIT: Please note that you are using a StackPane as your root parent. StackPane likes to position its children based on alignment attribute of the StackPane. As such, for my answer to work correctly, you must make your root parent a Group instead of a StackPane. A regular Pane can also work.
Group root = new Group();

Related

NotificationPane doesn't show up in Scene

I would like to display a NotificationPane after certain user actions. My application has multiple scenes and the NotificationPane should be showed up in the currently active scene.
The whole thing works with Notification, it pops up when I need it.
But I can't figure out how to make this work for NotificationPane.
Steps I made so far:
I tryed to put NotificationPane directly to my scene and call
show() - it works.
Now the Idea is to get the current pane by calling
stage.getScene().getRoot(), wrap it to NotificationPane and then call
show() - it doesn't work and I have no idea why.
((BorderPane) pane).setCenter(new Label("TEST")); this line is replacing buttons with text label, so stage.getScene().getRoot() is returning the right object
I made a simple program to test the behaviour. One button to call NotificationPane.
Any suggestions?
Here is my test program:
package application;
import org.controlsfx.control.NotificationPane;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button notificationPaneButton = new Button("NotificationPane");
notificationPaneButton.setOnAction(e -> showNotificationPane(primaryStage, "Notification text"));
VBox vbox = new VBox(5);
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(notificationPaneButton);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(vbox);
primaryStage.setTitle("Notifications test");
primaryStage.setScene(new Scene(borderPane, 300, 200));
primaryStage.show();
}
public void showNotificationPane(Stage stage, String message) {
Parent pane = stage.getScene().getRoot();
// ((BorderPane) pane).setCenter(new Label("TEST"));
NotificationPane notificationPane = new NotificationPane(pane);
notificationPane.setText(message);
if (notificationPane.showingProperty().get()) {
notificationPane.hide();
System.err.println("hide");
} else {
notificationPane.show();
System.err.println("show");
}
}
}
Ok, I see the problem now. Wrapping current pane is not enough, I also have to add the NotificationPane to the scene. Right?
Anyway my current solution is following:
get current scene
get current pane
wrap pane
replace current scene with the new one
To avoid wrapping NotificationPane multiple times I check if current pane is already a NotificationPane and then call show().
public void showNotificationPane(Stage stage) {
Scene scene = stage.getScene();
Parent pane = scene.getRoot();
if (!(pane instanceof NotificationPane)){
NotificationPane notificationPane = new NotificationPane(pane);
scene = new Scene(notificationPane, scene.getWidth(), scene.getHeight());
stage.setScene(scene);
notificationPane.show();
} else {
((NotificationPane)pane).show();
}
}

Application with BorderPane doesn't display simple button

very new to JavaFX I'm following a simple tutorial here
I created a new JavaFX project but it has a BorderPane as a default rather than a StackPane as the tutorial says, so I left it there.
The application only has a button on it and if I use the BorderPane the button isn't displayed.
If I change it to StackPane the button shows up.
Thinking that for some reason the BorderPane was clipping something off, I made the application windows full size, but I still couldn't see the button.
Here is the code with the BorderPane the one that doesn't display the button:
package application;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setTitle("This is a test!");
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!");
}
});
root.getChildren().add(btn);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Any idea?
Take a look at the docs about BorderPane:
BorderPane lays out children in top, left, right, bottom, and center
positions.
Therefore you need to use stuff like:
borderPane.setTop(toolbar);
borderPane.setCenter(appContent);
borderPane.setBottom(statusbar);
In your case root.getChildren().add(btn); should be for example root.setCenter(btn);.

Custom minimize button do not call MouseLeave event

I have a simple stage with StageStyle.TRANSPARENT (no default buttons).
Therefore I tried to create my own custom buttons, represented each by an ImageView with the next events activated: setOnMouseEntered, setOnMouseExited and of course setOnMouseClicked.
Problem is for the Minmized Button. Is a simple implementation like below
ImageView.setOnMouseClicked((MouseEvent event) -> {
stage.setIconified(true);
});
Lets imagine that my ImageView is a White rectangle. On mouse enter event, it changes its color into Black. On mouse exit, it is going back to White color.
When the ImageView is clicked, the window will be minimized, everything perfectly workable until now.
Problem is when the application is restored (maximized), the Minimized custom button is stuck with color Black (the color that represent the button is hovered), instead of White (default color when is not focused).
P.S. it seems that everything like relocate, setImage etc. inside the onMouseClicked handler is cut by the the setInconified(true);
Any help would be most appreciated.
Thank you for your time of reading this.
Updates to clear a bit the question
The normal print-screen image (when it is not hovered)
The hover print-screen (when it is hovered)
As you can observe, everything works perfectly. In the moment when "-" button (minimize button) is pressed, when the application is restored, it will remain stuck in hover mode, until the mouse cursor will hover again the button (then everything comes back to normal). Sadly neither CSS approach or event listeners on image view dose not seems to solve this issue.
Update code loaded
This is a simple one source file with just a button that call minimize
package application;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
private Scene scene;
private Stage stage;
#Override
public void start(Stage stage) {
try {
this.stage = stage;
stage.initStyle(StageStyle.TRANSPARENT);
stage.setAlwaysOnTop(true);
stage.setFullScreen(true);
stage.setFullScreenExitHint("");
createScene(stage);
stage.setScene(scene);
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
private void createScene(Stage stage) {
Pane layer = new Pane();
layer.setPickOnBounds(false);
scene = new Scene(layer, 800, 600);
scene.getStylesheets().add("application/application.css");
layer.getChildren().add(buildMinimizeImage());
}
private ImageView buildMinimizeImage() {
ImageView imv = new ImageView();
int width = 43 ;
int height = 36;
imv.setId("myImage");
imv.setFitWidth(width);
imv.setFitHeight(height);
imv.setOnMouseClicked((MouseEvent event) -> {
stage.setIconified(true);
});
imv.relocate(100, 100);
return imv;
}
public static void main(String[] args) {
launch(args);
}
}
And the application.css is very simple as well
#myImage
{
-fx-image: url("minimize.png");
}
#myImage:hover
{
-fx-image: url("minimizeIn.png");
}
Issue is reproducible on Ubuntu 14.04 and Windows 10. I do not think is an OS problem
RESOLVED
Please find enclose the Harry Mitchel solution (thank you one more time for it). It is perfectly workable.
If you want to fix the code from above I by adding the setOnMousePressed event.
imv.setOnMousePressed((MouseEvent event) -> {
imv.setImage(image);
});
You can listen to the maximized property of the Stage class. Inside the changed() method, set the ImageView's image.
stage.maximizedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
//Display the desired icon here.
}
});
Here is a custom minimize button. You provide the two images and the stage as parameters. When the mouse is not over the button, it will show the image referenced in the constructor's first parameter. When the mouse is over the button, it will show the image referenced in the constructor's second parameter. When you click the image the stage will be minimized.
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class MinimizeButton extends Button {
/**
*
* #param img the image when the button is NOT selected
* #param imgHover the image when button is selected
* #param stage the stage that will be minimized
*/
public MinimizeButton(Image img, Image imgHover, Stage stage) {
ImageView imgView = new ImageView(img);
this.setGraphic(imgView);
this.addEventHandler(MouseEvent.MOUSE_ENTERED, (MouseEvent e) -> {
imgView.setImage(imgHover);
});
this.addEventHandler(MouseEvent.MOUSE_EXITED, (MouseEvent e) -> {
imgView.setImage(img);
});
this.setOnAction((ActionEvent event) -> {
stage.setIconified(true);
imgView.setImage(img);
});
}
}
Here is an example app that uses the MinimizeButton class.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class CustomMinimize extends Application {
#Override
public void start(Stage stage) {
Image imgWhite = new Image(getClass().getResourceAsStream("imgWhite.png")); //your image here
Image imgGreen = new Image(getClass().getResourceAsStream("imgGreen.png")); //your hover image here
MinimizeButton btnMinimize = new MinimizeButton(imgWhite, imgGreen, stage);
btnMinimize.setStyle("-fx-background-color: black;");
btnMinimize.setPrefSize(50, 50);
Button btnExit = new Button("X");
btnExit.setMinSize(50,50);
btnExit.setOnAction((ActionEvent event) -> {
System.exit(0);
});
btnExit.setStyle("-fx-background-color: black;");
HBox hBox = new HBox();
hBox.setSpacing(2);
hBox.getChildren().addAll(btnMinimize, btnExit);
AnchorPane anchorPane = new AnchorPane();
anchorPane.getChildren().addAll(hBox);
AnchorPane.setRightAnchor(hBox, 5.0);
AnchorPane.setTopAnchor(hBox, 5.0);
Scene scene = new Scene(anchorPane);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Your question is not very clear (not that it is very unclear though), so I will attempt to solve your problem.
I am assuming that your color change is done through ImageView.setOnMouseEntered() and ImageView.setOnMouseExited(). If this is so, you should instead use CSS.
.myImageView
{
-fx-image: url("my_white_image.png");
}
.myImageView:hovered
{
-fx-image: url("my_black_image.png");
}
For the things in your "PS" section, I couldn't understand, so I would not be able to give any advice on that.

javafx - Dynamically resize node from within

I want to create simple gridPane, which would change its size after clicking a button. I have this code:
public class NewSimScene extends GridPane{
Button testButton;
public NewSimScene(){
setPrefSize(500, 500);
testButton = new Button("TEST");
testButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
Node source = (Node) t.getSource();
NewSimScene pane = (NewSimScene) source.getParent();
pane.setPrefSize(100, 100);
pane.setMaxSize(100, 100);
}
});
getChildren().add(testButton);
}
}
When I'm debugging, I can see that prefHeight and prefWidth values are changed, but there isn't any change in pane appearance. What could be a problem?
I don't know if it's relevant, but I'm running this pane in a separate scene, triggered from MenuBar.
there is no issue in your code, the Grid Pane's getting re-sized. Please have a look at the following code and feel free to comment if you are looking for something else !
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class TableViewSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
GridPane grid = new NewSimScene();
((Group) scene.getRoot()).getChildren().addAll(grid);
stage.setScene(scene);
stage.show();
}
public class NewSimScene extends GridPane {
Button testButton;
public NewSimScene() {
setPrefSize(500, 500);
setStyle("-fx-background-color: palegreen;");
testButton = new Button("TEST");
testButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
Node source = (Node) t.getSource();
NewSimScene pane = (NewSimScene) source.getParent();
pane.setPrefSize(100, 100);
}
});
getChildren().add(testButton);
}
}
}

Waiting for Scene repaint

Is there any possible way to wait for the scene to repaint?
My problem is, that i want to add a Note to a Pane with getChildren().add() and then to fire an event on this Node with Node.fireEvent(event).
But the event is not performed. I think the problem is, that the scene was not repainted at the point of the fireevent and so the Node is not a part of the new Scene at this time.
So the best way would be to wait for the scene to repaint and then fire the event i think.
I do not know which is the UI Component you has been used here, but, try to find out the invalidate() method (or something like that) of your component to make it update the screen after all.
Can you post code? This works fine for me:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class NewNodeEventTest extends Application {
#Override
public void start(Stage primaryStage) {
HBox root = new HBox(5);
Button newNodeButton = new Button("Add button");
newNodeButton.setOnAction(event -> {
Button newButton = new Button("Button");
newButton.setOnAction(e -> System.out.println("New button pressed"));
root.getChildren().add(newButton);
ActionEvent evt = new ActionEvent(newButton, newButton);
newButton.fireEvent(evt);
});
root.getChildren().add(newNodeButton);
Scene scene = new Scene(root, 250, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Categories