JavaFX: Window closing event prevents button action - button

I did implement a popup window in a new stage and I'm now trying to close it, no matter where I click (excluding the popup itself). This works just fine. Although the popup windows disappears when I click on another element (e.g. a button) on the background, I'd still like to get the event for the button. Any suggestions on how to achieve this? I put together a short example of the situation.
package application;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
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.setScene(scene);
// the popup
Pane p = new Pane();
p.setPrefSize(100, 100);
p.setStyle("-fx-background-color: #660066");
final Stage popUp = new Stage();
Scene popUpScene = new Scene(p);
popUp.setScene(popUpScene);
Button btnShow = new Button("Show popUp");
root.setCenter(btnShow);
btnShow.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event) {
ChangeListener stageFocusListener = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> arg0, Boolean oldPropertyValue, Boolean newPropertyValue) {
if (!newPropertyValue) {
popUp.hide();
}
}
};
popUp.focusedProperty().addListener(stageFocusListener);
popUp.show();
}
});
Button btnTest = new Button("test");
root.setRight(btnTest);
btnTest.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event) {
System.out.println("Button test clicked");
}
});
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}

You can put the hiding event to the queue without disturbing the event handling procedure of the button
if (!newPropertyValue) {
Platform.runLater(new Runnable() {
#Override
public void run() {
popUp.hide();
}
});
}

Related

javafx checkbox clicked while key pressed will not change state

I have a very simple problem, which I can't find in any other posts :
In javafx15 / java15.0.1, I am trying to click a Checkbox while pressing, for example, the CONTROL key... State is not changing.
I tried to catch the key (with a key event on the checkbox), and I do catch the control key pressed... But state of checkbox just does not change if a key is pressed simultaniously.
How to get this to just work in a transparent way ?
Here is a most basic simple code to illustate the problem :
package checkboxkeypressed;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.stage.Stage;
public class CheckboxWhileKeyPressedNotWorking extends Application {
private CheckBox checkbox = new CheckBox();
#Override
public void start(Stage primaryStage) {
checkbox.setText("Click me while pressing a key...");
Scene scene = new Scene(checkbox, 200, 50);
primaryStage.setTitle("Checkbox cannot be ticked while a key is pressed !!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Any help would be greatly appreciated.
Improving on the solution offered by etuygar:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.stage.Stage;
public class CheckboxWhileKeyPressedNotWorking extends Application {
private final CheckBox checkbox = new CheckBox();
private boolean isControlKeyDown = false;
#Override
public void start(Stage primaryStage) {
checkbox.setText("Click me while pressing <CNTRL> key");
checkbox.setOnMouseClicked(e->{
if(isControlKeyDown){
checkbox.fire(); //change check box state
}
});
Scene scene = new Scene(checkbox, 300, 50);
scene.setOnKeyPressed(keyEvent -> {
isControlKeyDown = keyEvent.isControlDown();
});
scene.setOnKeyReleased(keyEvent -> {
isControlKeyDown = keyEvent.isControlDown();
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You can do something like this.
package checkboxkeypressed;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.input.KeyCode;
import javafx.stage.Stage;
public class CheckboxWhileKeyPressedNotWorking extends Application {
private CheckBox checkbox = new CheckBox();
private boolean ctrlOk, lastState;
#Override
public void start(Stage primaryStage) {
checkbox.setText("Click me while pressing a key...");
Scene scene = new Scene(checkbox, 200, 50);
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.CONTROL) ctrlOk = true;
});
scene.setOnKeyReleased(event -> {
if (event.getCode() == KeyCode.CONTROL) ctrlOk = false;
});
checkbox.setOnMouseClicked(event -> {
if (!ctrlOk) {
checkbox.setSelected(lastState);
} else {
checkbox.setSelected(!checkbox.isSelected());
lastState = checkbox.isSelected();
}
});
primaryStage.setTitle("Checkbox cannot be ticked while a key is pressed !!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Thank you soooooo much #miss-chanandler-bong !
So would this answer be ok ? I'm forcing the checkbox to be armed, when armed is changing...
package checkboxkeypressed;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
public class CheckboxWhileKeyPressed extends Application {
private CheckBox checkbox = new CheckBox();
#Override
public void start(Stage primaryStage) {
checkbox.setText("Click me while pressing a key...");
checkbox.armedProperty().addListener(changeListener -> checkbox.arm());
checkbox.arm();
checkbox.setOnKeyPressed(keyEvent -> processKeyPressed(keyEvent));
Scene scene = new Scene(checkbox, 200, 50);
primaryStage.setTitle("Checkbox CAN NOW be ticked while a key is pressed !!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void processKeyPressed(KeyEvent keyEvent) {
if (keyEvent.getCode().equals(KeyCode.CONTROL)) {
System.out.println("Key IS control");
} else {
System.out.println("Key IS NOT control : '" + keyEvent.getCode().getName() + "'");
}
}
public static void main(String[] args) {
launch(args);
}
}

Regain javafx focus

I have an application which runs in background. With jkeymaster I have registered a global hotkey to be fired although my application is not the active one. This is working. I now want to switch my background app in foreground again. Tried with this code:
import com.tulskiy.keymaster.common.HotKey;
import com.tulskiy.keymaster.common.HotKeyListener;
import com.tulskiy.keymaster.common.Provider;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
public class TestClientNotWorking extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws AWTException {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Btn said 'Hello World!'");
}
});
HBox root = new HBox(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
primaryStage.requestFocus();
Provider provider = Provider.getCurrentProvider(false);
provider.register(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
InputEvent.ALT_MASK | InputEvent.SHIFT_MASK),
new HotKeyListener() {
#Override
public void onHotKey(HotKey hotKey) {
Platform.runLater(new Runnable() {
#Override
public void run() {
System.out.println("Recievd");
primaryStage.toFront();
btn.requestFocus();
}
});
}
});
}
}
What happens:
My stage is brought back to foreground, but the focus still remains in the app, were it was before and is not transferred to my primaryStage. Can you please help my how I could gain this?
Thanks in advance
I compiled and run the code. As far as I understand your expectations are:
start application, register key hook
minimize jfx window
press key combination (alt shift space in your case)
result: jfx window is in foreground state, 'Hello World' button is focused
btn.requestFocus() works properly on my OS without any changes but my guess you need to try setIconified\setMaximized instead of primaryStage.toFront();
I changed a little bit your code pls try it and let us know if it works as you want.
public class TestClientNotWorking extends Application {
private final Provider provider = Provider.getCurrentProvider(false);
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) throws AWTException {
final Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent event) {
System.out.println("Btn said 'Hello World!'");
}
});
final HBox root = new HBox(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
root.requestFocus();// remove focus from btn
final KeyStroke stroke = KeyStroke.getKeyStroke("ctrl SPACE");// stroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_MASK | InputEvent.ALT_MASK);
provider.register(stroke, new HotKeyListener() {
#Override
public void onHotKey(final HotKey hotKey) {
Platform.runLater(new Runnable() {
#Override
public void run() {
System.out.println("Recievd");
primaryStage.setIconified(false); // primaryStage.toFront();
btn.requestFocus(); // set focus on btn
}
});
}
});
}
#Override
public void stop() throws Exception {
super.stop();
provider.stop();
}
}

JavaFX - StackPane with Pane as EventDispatcher

In the event dispatcher pane occurs an mouse pressed event.
The pane one should show the context menu of it's combobox when a event occurs.
That works fine if the event is only dipatched to pane one.
When the event is dipatched to pane one and pane two the context menu of pane one doesn't show up.
I suppose it has something to do with the event tail and event consuming.
Until now i doesn't had a look at the EventDispatcher Class of the JDK itself.
Here is what i got so far:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
*
* #author Robert
*/
public class EventDispatcherExample extends Application {
private Group root;
private StackPane cStackPane;
private Pane cPaneEventDispatcher;
private Pane cPaneOne;
private ComboBox cComboBox;
private Pane cPaneTwo;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
root = new Group();
cStackPane = new StackPane();
cStackPane.setPrefHeight(200.0);
cStackPane.setPrefWidth(200.0);
cPaneEventDispatcher = new Pane();
cPaneEventDispatcher.setPrefHeight(200.0);
cPaneEventDispatcher.setPrefWidth(200.0);
cPaneEventDispatcher.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane ED.");
cPaneOne.fireEvent(event);
cPaneTwo.fireEvent(event);
}
});
cPaneOne = new Pane();
cPaneOne.setPrefHeight(200.0);
cPaneOne.setPrefWidth(200.0);
cPaneOne.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane One.");
cComboBox.show();
}
});
ObservableList<String> observableList = FXCollections.observableArrayList();
observableList.add("1");
observableList.add("2");
observableList.add("3");
cComboBox = new ComboBox();
cComboBox.setLayoutX(50.0);
cComboBox.setLayoutY(50.0);
cComboBox.setPrefHeight(30.0);
cComboBox.setPrefWidth(100.0);
cComboBox.setItems(observableList);
cPaneTwo = new Pane();
cPaneTwo.setPrefHeight(200.0);
cPaneTwo.setPrefWidth(200.0);
cPaneTwo.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane Two.");
//Something will happen because of selected item in Combo Box of pane one...
}
});
cPaneOne.getChildren().add(cComboBox);
// add the nodes in reverse order
cStackPane.getChildren().add(cPaneTwo);
cStackPane.getChildren().add(cPaneOne);
cStackPane.getChildren().add(cPaneEventDispatcher);
root.getChildren().add(cStackPane);
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Any ideas how to handle this?
After some ideas I got a solution that at least works:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
/**
*
* #author Robert
*/
public class EventDispatcherExample extends Application {
private Group root;
private StackPane cStackPane;
private Pane cPaneEventDispatcher;
private Pane cPaneOne;
private ComboBox cComboBox;
private boolean cComboBoxClicked = false;
private Pane cPaneTwo;
public static void main(String[] args) {
launch(args);
}
public boolean isComboBoxClicked() {
if (cComboBoxClicked == true) {
cComboBox.show();
} else {
cComboBox.hide();
}
return cComboBoxClicked;
}
#Override
public void start(Stage primaryStage) throws Exception {
root = new Group();
cStackPane = new StackPane();
cStackPane.setPrefHeight(200.0);
cStackPane.setPrefWidth(200.0);
cPaneEventDispatcher = new Pane();
cPaneEventDispatcher.setPrefHeight(200.0);
cPaneEventDispatcher.setPrefWidth(200.0);
cPaneEventDispatcher.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane ED.");
cPaneOne.fireEvent(event);
cPaneTwo.fireEvent(event);
}
});
cPaneOne = new Pane();
cPaneOne.setPrefHeight(200.0);
cPaneOne.setPrefWidth(200.0);
cPaneOne.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane One.");
Rectangle rect = new Rectangle(cComboBox.getLayoutX(), cComboBox.getLayoutY(),
cComboBox.getPrefWidth(), cComboBox.getPrefHeight());
cComboBoxClicked = rect.contains(event.getX(), event.getY());
}
});
ObservableList<String> observableList = FXCollections.observableArrayList();
observableList.add("1");
observableList.add("2");
observableList.add("3");
cComboBox = new ComboBox();
cComboBox.setLayoutX(50.0);
cComboBox.setLayoutY(50.0);
cComboBox.setPrefHeight(30.0);
cComboBox.setPrefWidth(100.0);
cComboBox.setItems(observableList);
cComboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
// if cComboBoxSelectedIndex == 1 do Color on pane two
// if cComboBoxSelectedIndex == 2 do Size on pane two
// if cComboBoxSelectedIndex == 3 do ...
//System.out.println("newValue " + newValue);
}
});
cPaneTwo = new Pane();
cPaneTwo.setPrefHeight(200.0);
cPaneTwo.setPrefWidth(200.0);
cPaneTwo.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane Two.");
boolean cComboBoxClicked = isComboBoxClicked();
if (cComboBoxClicked){
//System.out.println("Skip code internally managed by pane two.");
return;
}
// Internal code of pane two
//...
}
});
cPaneOne.getChildren().add(cComboBox);
// add the nodes in reverse order
cStackPane.getChildren().add(cPaneTwo);
cStackPane.getChildren().add(cPaneOne);
cStackPane.getChildren().add(cPaneEventDispatcher);
root.getChildren().add(cStackPane);
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Perhaps a better approach comes up during the time of someone else.
Am looking forward...

Jfxtra window bring it forward / internal window

I have an internal jfxtra window. On clicking a button, I want to bring it forward.
The code that I have tried :
window w = new window("mdi win");
private Stage primaryStage;
private BorderPane rootLayout;
...
public void win() {
Parent bla = FXMLLoader.load(getClass().getResource("bla.fxml"));
w.getContentPane().getChildren().add(bla);
rootLayout.getChildren().add(w);
}
private void wfront(ActionEvent event) throws Exception {
w.isMoveToFront(); // is not?
}
How to make it possible?
So you made me curious and I went through the JFXtras docs. I came to know that Window in Jfxtras extends Control. So there is a method called toFront which can be fired on it. To show this I have created a sample for you.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import jfxtras.scene.control.window.Window;
public class NewWindow extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane stackPane = new StackPane();
Button button = new Button("Click Me to show Window !");
Window window = new Window("Cick Me to bring me to front");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
window.toFront();
window.setTitle("I am on the Front");
}
});
window.setPrefSize(200, 200);
stackPane.getChildren().addAll(window, button);
Scene scene = new Scene(stackPane, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Let me know, if you are looking for something else !

Back button that will open main Stage in Javafx 2

I have 2 screens in application. First screen(MainController class) which opens with running application from Eclipse.
And second screen(SecondController class) which opens on button located in first screen.
How can I make some kind of 'Back' button in second screen which will show back first screen?
I'm creating the visual part of the application in JavaFX Scene Builder if it matters.
Here is a small example, containing to screens, to show how you can achieve what you are looking for
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class TwoScreensWithInterchange extends Application {
#Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(loadScreenOne(), 200, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
public VBox loadScreenOne()
{
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
final Button button = new Button("Switch Screen");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
button.getScene().setRoot(loadScreenTwo());
}
});
Text text = new Text("Screen One");
vBox.getChildren().addAll(text, button);
vBox.setStyle("-fx-background-color: #8fbc8f;");
return vBox;
}
public VBox loadScreenTwo()
{
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
final Button button = new Button("Back");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
button.getScene().setRoot(loadScreenOne());
}
});
Text text = new Text("Screen Two");
vBox.getChildren().addAll(text, button);
return vBox;
}
}

Resources