JavaFX Wait for a click wherever on a screen - java

I have two buttons: buttonPoint1 and buttonPoint2. When I click one of the buttons it calls action method for this button. In this function I want program to wait for me to click wherever on a screen. Then I'll call MouseInfo.getPointerInfo().getLocation(). When I click outside my app window (e.g on other window), I lose focus on my app. How to make program to wait for click, don't hide, lose focus and give me response that I clicked?

One idea is using a transparent window like so
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
VBox vBox = new VBox(new Label("IMAGINE A BEAUTIFUL APPLICATION HERE"));
vBox.setAlignment(Pos.CENTER);
Button button = new Button("Im gonna steal your next click");
button.setOnAction(event -> {
System.out.println("Firing Button Action");
openTransparentWindow();
System.out.println("Button Action Finished Firing");
});
vBox.getChildren().add(button);
Scene scene = new Scene(vBox, 300, 150);
primaryStage.setScene(scene);
primaryStage.show();
}
private void openTransparentWindow(){
VBox vBox = new VBox();
Stage stage = new Stage();
stage.setScene(new Scene(vBox));
//stage.setOpacity(0.5f); //Uncomment if you want to test and see the stage
stage.setOpacity(0f);
stage.setMaximized(true);
vBox.setOnMouseClicked(event ->{
System.out.println("event.getX() = " + event.getX());
System.out.println("event.getY() = " + event.getY());
System.out.println("event.getScreenX() = " + event.getScreenX());
System.out.println("event.getScreenY() = " + event.getScreenY());
stage.close();
});
stage.showAndWait();
}
}

Related

Having some trouble with javafx alert box interactions

I'm trying to learn javafx and I'm having some trouble with alert box interactions. My code is pretty straightforward:
public static void display(String title, String message) {
Stage window = new Stage();
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle(title);
window.setMinWidth(250);
Label label = new Label();
label.setText(message);
Button closeButton = new Button("Close the window");
closeButton.setOnAction(e -> window.close());
VBox layout = new VBox(10);
layout.getChildren().addAll(label, closeButton);
layout.setAlignment(Pos.CENTER);
Scene scene = new Scene(layout);
window.setScene(scene);
window.showAndWait();
}
The problem is I can still somewhat interact with my initial window that I used to open the alert box, mainly clicking on the initial window brings it in front of the alert box. To my understanding, this shouldn't happen. Here is a gif demonstrating the issue: https://gyazo.com/0c2b69ec39f849227560fbdf2099c07c
Here is my code that calls the alert box
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("Alert Box test");
button = new Button("Click me");
button.setOnAction(e -> AlertBox.display("New Alert", "Don't forget to close this window!"));
StackPane layout = new StackPane();
layout.getChildren().add(button);
Scene scene = new Scene(layout, 300, 250);
window.setScene(scene);
window.show();
}
What am I doing wrong here or is that just the intended behavior?

Creating new Transparent Stage on Event JavaFX

I'm trying to create a new Stage when a button is pressed.
It works but the problem is that I'd like this Stage to be fully transparent and lets us see what's behind the screen.
Code
Dimension Sizescreen = Toolkit.getDefaultToolkit().getScreenSize();
//Main stage with option menu
Pane window = new Pane();
Scene scene = new Scene(window);
stage.setTitle("Notification Extender");
//Create the button SetLooker
Button SetLooker = new Button("Set Looker");
//Add a Event when pressed
SetLooker.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
//Create a sub-Stage
Pane subwindow = new Pane();
Scene subscene = new Scene(subwindow);
Stage substage = new Stage();
substage.setTitle("Notification Extender");
//Set this subStage Transparent
substage.initStyle(StageStyle.TRANSPARENT);
subscene.setFill(Color.TRANSPARENT);
substage.setWidth(Sizescreen.getWidth());
substage.setHeight(Sizescreen.getHeight());
substage.setX(0);
substage.setY(0);
//Create a a graphique element
Rectangle redrec = new Rectangle(120,40,50,50);
redrec.setStroke(Color.RED);
redrec.setStrokeWidth(2);
redrec.setFill(Color.TRANSPARENT);
//Add the graphique element to the sub-stage
subwindow.getChildren().add(redrec);
//Show the sub-stage
substage.setScene(subscene);
substage.show();
}
});
//Add the button to the main stage
window.getChildren().add(SetLooker);
//Show the main stage
stage.setScene(scene);
stage.show();
The problem is that when I press the button it shows the stage but it's not transparent at all it's completely white.
I've also tried to change the main Stage, but I cannot change it once it has been shown.
You also need to remove the background from the root of your new scene:
subwindow.setBackground(null);

how to lock the size of a Stage in java

I made a very simple java application with 3 scenes and 3 buttons.
button3 (b3) opens a popup but I set the image to lock size but the window can still be changed by using the mouse on one of the corners.
is there a way to lock the size so the curson can't edit this?
Here's the simplified code (just to get and idea)
public class Handler extends Application
{
#Override
public void start(Stage primaryStage) throws Exception {
Button button1 = new Button("AD");
VBox layout1 = new VBox(button1);
layout2.setAlignment(Pos.CENTER);
Pane layout2 = new Pane();
layout3.setStyle("-fx-background-image: url(ad.jpg);" +
"-fx-background-repeat: no-repeat;" +
"-fx-background-size: initial;");
Scene scene1 = new Scene(layout1);
Scene scene2 = new Scene(layout2);
//Stage (window)
primaryStage.setTitle("Basic Handler");
primaryStage.setScene(scene1);
primaryStage.setHeight(400);
primaryStage.setWidth(400);
primaryStage.show();
button1.setOnAction(event -> {
Stage PopUp = new Stage();
PopUp.setHeight(266);
PopUp.setWidth(474);
PopUp.setTitle("Sponsored AD");
PopUp.setScene(scene2);
PopUp.showAndWait();
});
}
}
Just add this in your stage :
PopUp.setResizable(false);

In Javafx when a window is opened how can i set it so that the user isnt able to open the same window again?

I tried a lot but just couldn't find any solution. At the moment the opened window(popup window) is always on top but the user can still access the main window. That's how it should be, but it shouldn't be possible to open the same popup window again.
Stage stage = new Stage();
stage.setTitle(panelTitle);
stage.setScene(new Scene(root));
stage.initModality(Modality.WINDOW_MODAL);
stage.setAlwaysOnTop(true);
stage.showAndWait();
Thank you in advance!
As LazerBanana said, I would disable the button that opens the window, and I would enable it when you close it.
Stage stage = new Stage();
button.setDisable(true);
stage.setTitle(panelTitle);
stage.setScene(new Scene(root));
stage.initModality(Modality.WINDOW_MODAL);
stage.setAlwaysOnTop(true);
stage.showAndWait();
// your logic here
button.setDisable(false);
An alternative solution to creating a new one each time is to create one and just setup and show.
public class Stack extends Application {
private final Stage popup = new Stage();
#Override
public void start(Stage stage) throws Exception {
BorderPane root = new BorderPane();
root.setPrefWidth(400);
root.setPrefHeight(200);
Button button = new Button("ClickMePopup");
root.setCenter(button);
button.setOnAction(
event -> {
if (!popup.isShowing()) {
// you dont set modality because after the stage is set to visible second time it will throw an exception.
// Again depends on what you need.
// popup.initModality(Modality.WINDOW_MODAL);
// this focuses the popup and main window is not clickable
// popup.initOwner(stage);
VBox dialogVbox = new VBox(20);
dialogVbox.getChildren().add(new Text("Some Dialog"));
Scene dialogScene = new Scene(dialogVbox, 300, 200);
popup.setScene(dialogScene);
// you can actually put all above into the method called initPopup() or whatever, do it once, and just show it here or just bind the property to the button.
popup.show();
}
});
Scene scene = new Scene(root);
stage.setTitle("Stack");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Or disable the button when clicked, but if your popup is not driven by the button or can be opened from other places the first idea would be a bit better in my opinion. Depends on what you need.
Or just create your own class and Springify it.

Is it possible to use to use a path transition on a dialog box in javafx?

So I'm running a little test application to see if this possible and then move it to my main project. The main idea is that the dialog box come from the top of the screen to the center and waits for a response from the the user. If they click no, the program terminates. If they click yes, the dialog box goes from the center to the top screen and is out of sight from the user.
package test;
import java.util.Optional;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.layout.VBox;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.VLineTo;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Test extends Application
{
#Override
public void start(Stage stage) throws Exception
{
VBox root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText("Look, a Confirmation Dialog");
alert.setContentText("Are you ok with this?");
Path path = new Path();
path.getElements().add(new MoveTo(300, -25));
path.getElements().add(new VLineTo(200));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(1500));
pathTransition.setPath(path);
pathTransition.setNode(alert); // This is where the problem lies.
pathTransition.setCycleCount(1);
pathTransition.play();
Scene scene = new Scene(root, 640, 480);
stage.setScene(scene);
stage.show();
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK)
{
Path path2 = new Path();
path2.getElements().add(new MoveTo(300, 200));
path2.getElements().add(new VLineTo(-25));
PathTransition pathTransition2 = new PathTransition();
pathTransition.setDuration(Duration.millis(1500));
pathTransition.setPath(path);
pathTransition.setNode(alert);
pathTransition.setCycleCount(1);
pathTransition.play();
}
else
{
stage.close();
}
}
public static void main(String[] args)
{
launch(args);
}
}
There's a way that you can move the alert dialog, by means of its yProperty(). Instead of a path transition we'll use a timeline to set this property. But since this is a read only property, we havet to use a DoubleProperty within the transition instead and use Alert.setY().
The first part of your question, sliding in the dialog, is easy. The second, sliding out, is more complex, since the dialog is closed inmediately once a button is clicked.
Solution 1. Just sliding in
We need the dialog dimensions and position, and for that we need to show it. This means it will be shown, and inmediately moved to the top of the screen.
So I'll change alert.showAndWait() for alert.show().
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Show Sliding In Alert Dialog");
btn.setOnAction(event -> {
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText("Look, a Confirmation Dialog");
alert.setContentText("Are you ok with this?");
ButtonBar buttonBar=(ButtonBar)alert.getDialogPane().lookup(".button-bar");
buttonBar.setDisable(true);
alert.initModality(Modality.APPLICATION_MODAL);
alert.show();
// now we can retrive alert bounds:
double yIni=-alert.getHeight();
double yEnd=alert.getY();
// and move alert to the top of the screen
alert.setY(yIni);
final DoubleProperty yProperty = new SimpleDoubleProperty();
yProperty.addListener((ob,n,n1)->alert.setY(n1.doubleValue()));
Timeline timeIn = new Timeline();
timeIn.getKeyFrames().add(
new KeyFrame(Duration.seconds(1.5),
e->buttonBar.setDisable(false),
new KeyValue(yProperty, yEnd,Interpolator.EASE_BOTH)));
timeIn.play();
alert.resultProperty().addListener((ob,r,r1)->{
if (r1 == ButtonType.OK){
// alert is closed and hidden in its final position
}
else{
primaryStage.close();
}
});
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
The listener in yProperty() allow us to set the position to the dialog within all the different positions interpolated during the transition.
Solution 2. Slide In and Out
This is a bit of a dirty solution, since involves using a second Alert dialog, given the original one is closed once the buttons are clicked. We'll add this second dialog behind the first one, and use it to create the slide out effect once the first one is closed.
The only side effect you will notice is a fast blink in the phase of showing the second and putting the first one on top of it.
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Show Sliding In Alert Dialog");
btn.setOnAction(event -> {
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText("Look, a Confirmation Dialog");
alert.setContentText("Are you ok with this?");
Alert alertOut = new Alert(AlertType.CONFIRMATION);
alertOut.setTitle("Confirmation Dialog");
alertOut.setHeaderText("Look, a Confirmation Dialog");
alertOut.setContentText("Are you ok with this?");
alertOut.initModality(Modality.NONE);
((Stage)alertOut.getDialogPane().getScene().getWindow()).setOpacity(0);
ButtonBar buttonBar=(ButtonBar)alert.getDialogPane().lookup(".button-bar");
buttonBar.setDisable(true);
alert.initModality(Modality.APPLICATION_MODAL);
alert.show();
// now we can retrive alert bounds:
double yIni=-alert.getHeight();
double yEnd=alert.getY();
// and move alert to the top of the screen
alert.setY(yIni);
final DoubleProperty yProperty = new SimpleDoubleProperty();
yProperty.addListener((ob,n,n1)->alert.setY(n1.doubleValue()));
Timeline timeIn = new Timeline();
timeIn.getKeyFrames().add(
new KeyFrame(Duration.seconds(1.5),
e->{
buttonBar.setDisable(false);
// show second dialog
alertOut.show();
// move to front the first one
((Stage)alert.getDialogPane().getScene().
getWindow()).toFront();
}, new KeyValue(yProperty, yEnd,Interpolator.EASE_BOTH)));
timeIn.play();
alert.resultProperty().addListener((ob,r,r1)->{
if (r1 == ButtonType.OK){
// show second dialog
((Stage)alertOut.getDialogPane().getScene().getWindow()).setOpacity(1);
ButtonBar buttonBarOut=(ButtonBar)alertOut.getDialogPane().lookup(".button-bar");
buttonBarOut.setDisable(true);
final DoubleProperty yPropertyOut = new SimpleDoubleProperty(yEnd);
yPropertyOut.addListener((ov,n,n1)->alertOut.setY(n1.doubleValue()));
// Create slide out transition
Timeline timeOut = new Timeline();
timeOut.getKeyFrames().add(
new KeyFrame(Duration.seconds(1.5),
e->alertOut.close(),
new KeyValue(yPropertyOut, yIni,Interpolator.EASE_BOTH)));
timeOut.play();
}
else{
alertOut.close();
primaryStage.close();
}
});
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
EDIT
Solution 2. Improved Slide In and Out
I've found the way to use one single dialog, and provide the slide out effect too.
All it takes is trap the click action on the selected button, consume the event, add the slide out transition there, and hide the dialog on finished.
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Show Sliding In Alert Dialog");
btn.setOnAction(event -> {
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText("Look, a Confirmation Dialog");
alert.setContentText("Are you ok with this?");
ButtonBar buttonBar=(ButtonBar)alert.getDialogPane().lookup(".button-bar");
buttonBar.setDisable(true);
alert.initModality(Modality.APPLICATION_MODAL);
alert.show();
// now we can retrive alert bounds:
double yIni=-alert.getHeight();
double yEnd=alert.getY();
// and move alert to the top of the screen
alert.setY(yIni);
buttonBar.getButtons().stream().filter(b->((Button)b).isDefaultButton()).findFirst()
.ifPresent(b->((Button)b).addEventFilter(EventType.ROOT,
e->{
if(e.getEventType().equals(ActionEvent.ACTION)){
e.consume();
final DoubleProperty yPropertyOut = new SimpleDoubleProperty(yEnd);
yPropertyOut.addListener((ov,n,n1)->alert.setY(n1.doubleValue()));
Timeline timeOut = new Timeline();
timeOut.getKeyFrames().add(new KeyFrame(Duration.seconds(1.5), t->alert.close(),
new KeyValue(yPropertyOut, yIni,Interpolator.EASE_BOTH)));
timeOut.play();
}
}));
final DoubleProperty yProperty = new SimpleDoubleProperty();
yProperty.addListener((ob,n,n1)->alert.setY(n1.doubleValue()));
Timeline timeIn = new Timeline();
timeIn.getKeyFrames().add(new KeyFrame(Duration.seconds(1.5), e->{
buttonBar.setDisable(false);
},new KeyValue(yProperty, yEnd,Interpolator.EASE_BOTH)));
timeIn.play();
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}

Categories

Resources