I want to automatically show an alert box again after closing it, this is based on a certain condition.
Here's my code:
protected void showInputDialog()
{
FXMLLoader loader = new FXMLLoader(getClass().getResource("AddRecordDialog.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
AddRecordDialogController addRecordDialogController = (AddRecordDialogController)loader.getController();
addRecordDialogController.setAddNewSalesDialogController(this);
addRecordDialogController.setInvoice(this.invoice);
this.addRecordDialog = new Stage();
this.addRecordDialog.setTitle("Add Record");
this.addRecordDialog.initModality(Modality.APPLICATION_MODAL);
this.addRecordDialog.initOwner(root.getScene().getWindow());
this.addRecordDialog.setScene(scene);
this.addRecordDialog.sizeToScene();
this.addRecordDialog.setResizable(false);
//Event handler for when a Window is closed.
this.addRecordDialog.setOnHiding(new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent we)
{
if(nextItem == true)
showInputDialog();
nextItem = false;
}
});
this.addRecordDialog.showAndWait();
}
The second dialog shows up but the first dialog doesn't disappear.
The dialog is programmatically closed with a stage.close(); somewhere else.
I saw here that you need to call the setOnHiding method for a programmatically closing event.
If I remove the event handler the previous stage will close.
But I want to open a new instance of that stage again after it is closed.
Please help.
Edit: ...yes I checked for the nextItem variable it was true,
I used System.out.println("Next Item: " + nextItem);
Based on kendavidson's comment, I have found the solution.
I've changed the code to:
this.addRecordDialog.setOnHidden(new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent we)
{
System.out.println("Next Item: " + nextItem);
if(nextItem == true)
{
nextItem = false;
showInputDialog();
}
}
});
Thanks kendavidson.
Related
I have a chart. A double click on a table row opens a window in which I can edit the information of the row. Double-clicking on another line opens the corresponding window. Everything works as it should, except that each window should open only once. If it is already open, the window should return to the foreground. Unfortunately I can't see what's wrong here and would be grateful for a hint
private void loadTable() {
// I omitted the part where the table is populated.
mytable.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.isPrimaryButtonDown() && event.getClickCount() == 2) {
TableItem table_item = table_view.getSelectionModel().getSelectedItem();
try {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/application/Popup.fxml"));
Scene scene = new Scene(fxmlLoader.load());
Stage stage = new Stage();
stage.setScene(scene);
MyController controller = fxmlLoader.<MyController>getController();
boolean result = controller.init(stage, table_item);
if (stage.isShowing()) {
stage.toFront();
System.out.println("Show this popup.");
} else {
if (result) {
stage.show();
System.out.println("Open this popup.");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
Try adding
stage.initModality(Modality.WINDOW_MODAL);
after
Stage stage = new Stage();
This will make the Parent stage unclickable until this stage is closed.
I need to detect if a node is currently displaying.
I.e. if my Node is in a TabPane, I need to know if it is in a selected tab or not.
In the example, I want to know when the HBox is displaying.The visibleProperty and managedProperty of Node, does not seem to help me:
public class VisibleTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabpane = new TabPane();
tabpane.getTabs().add(new Tab("Tab1", new Label("Label1")));
HBox hbox = new HBox(new Label("Label2"));
hbox.setStyle("-fx-background-color: aquamarine;");
hbox.visibleProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Hbox visible changed. newValue: " + newValue);
});
hbox.managedProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Hbox managed changed. newValue: " + newValue);
});
Tab tab2 = new Tab("tab2", hbox);
tabpane.getTabs().add(tab2);
primaryStage.setScene(new Scene(tabpane));
primaryStage.setWidth(600);
primaryStage.setHeight(500);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I know, it is possible to listen on the selectedProperty state of the tab, but this does not solve my real problem.
Node.impl_isTreeVisible() does what I want, but this is depricated API.
Any ideas?
------------------------------------ update--------------------
I realize the code example above does not explain well what I'm trying to accomplish. Below is some Swing code that kind of demonstrates what I am trying to accomplish in JavaFX. Detect if the JComponent/Node is visible/shown, and based on that state, start or stop background processes. How would the constructor look like if it was a javaFX class.
public class SwingVisible extends JComponent {
String instanceNR;
Thread instanceThread;
boolean doExpensiveStuff = false;
public SwingVisible(String instanceNR) {
this.instanceNR = instanceNR;
this.setLayout(new FlowLayout());
this.add(new JLabel(instanceNR));
instanceThread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (doExpensiveStuff) {
/*
* do expensive stuff.
*/
System.out.println(instanceNR + " is visible " + isVisible());
}
}
}
});
/*
* How to do this in FX?
*/
addComponentListener(new ComponentAdapter() {
#Override
public void componentShown(ComponentEvent e) {
if (!instanceThread.isAlive()) {
instanceThread.start();
}
doExpensiveStuff = true;
}
#Override
public void componentHidden(ComponentEvent e) {
doExpensiveStuff = false;
}
});
}
public static void main(String[] args) {
/*
* This block represents code that is external to my library. End user
* can put instances of SwingVisible in JTabbedPanes, JFrames, JWindows,
* or other JComponents. How many instances there will bee is not in my
* control.
*/
JTabbedPane jtp = new JTabbedPane();
jtp.add("tab1", new SwingVisible("1"));
jtp.add("tab2", new SwingVisible("2"));
jtp.add("tab3", new SwingVisible("3"));
JFrame f = new JFrame("test");
f.setContentPane(jtp);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Output when tab1 is selected:
1 is visible true
1 is visible true
1 is visible true
...
Output when tab2 is selected:
2 is visible true
2 is visible true
2 is visible true
...
You can use Tab's selectedProperty to know if it is selected or not, and by extension if its content is visible or not. It is a boolean property.
I've converted your Swing code to JavaFX based on your initial JavaFX example:
public class VisibleTest extends Application {
public class FXVisible extends Tab {
FXVisible(String id) {
super(id, new Label(id));
Timeline thread = new Timeline(
new KeyFrame(Duration.ZERO, e -> {
if (isSelected()) {
// do expensive stuff
System.out.println(id + " is visible");
}
}),
new KeyFrame(Duration.seconds(1))
);
thread.setCycleCount(Timeline.INDEFINITE);
selectedProperty().addListener((selectedProperty, wasSelected, isSelected) -> {
if (isSelected) {
if (thread.getStatus() != Status.RUNNING) {
System.out.println(id + " starting thread");
thread.play();
}
}
// else, it is not selected -> content not shown
});
}
}
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabpane = new TabPane();
tabpane.getTabs().add(new FXVisible("1"));
tabpane.getTabs().add(new FXVisible("2"));
tabpane.getTabs().add(new FXVisible("3"));
// add as many as you want
primaryStage.setScene(new Scene(tabpane));
primaryStage.setWidth(600);
primaryStage.setHeight(500);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I replaced your thread with a JavaFX Timeline. Your question is not about this topic so I won't go into details here, though it's self explanatory.
I don't understand why in the Swing example you have a listener changing a boolean that indicates if the component is visible or not when you can just call isVisible() directly in the thread (see comments below for a note about threading). This is why in my code above I took the approach of checking isSelected() directly with no self-declared boolean. If you need to revert to your design it's rather straightforward. Just noting this for clarity.
The ComponentListener can be replaced with a change listener on selectedProperty() and querying the new value. Just be sure that your example does what it's supposed to do: the first time the tab is selected the thread/timer starts. After that the thread/timer does nothing. You might have wanted to pause the computation for non-displaying content. Again, just noting it because it seemed like a potential mistake to me, otherwise you're fine.
Updated answer.
tab2.getContent().isVisible();
It seems to me that my original answer is correct. If not, you need to ask your question in a better way. You want to know when the hbox is visible(meaning you can see the hbox on the screen).
tabpane.getSelectionModel().selectedItemProperty().addListener((obsVal, oldTab, newTab)->{
System.out.println(newTab.getText());
if(newTab.getText().equals("tab2"))
{
//You can use this code to set the hbox visibility, that way you can force the behavior you are looking for.
hbox.setVisible(true);
System.out.println("hbox is visible!");
}
else
{
//You can use this code to set the hbox visibility, that way you can force the behavior you are looking for.
hbox.setVisible(false);
System.out.println("hbox is not visible!");
}
});
From quick checking this seemed to work for both checking the window is showing and that the tab it is in is displaying. I have also checked and it seems to work as expected for titled panes too that are collapsible.
public static boolean detectVisible( Node node )
{
Node current = node;
while( current != null ) {
if( !current.isVisible() ) {
return false;
}
current = current.getParent();
}
Window window = Optional.of( node ).map( Node::getScene ).map( Scene::getWindow ).orElse( null );
if( window == null ) {
return false;
}
if( window instanceof Stage && ( (Stage) window ).isIconified() ) {
return false;
}
return window.isShowing();
}
I have an application that looks like the following:
When a user clicks on the deck of cards, it opens up a new Stage.
This stage can be closed in one of two ways:
Right click the stage.
Click outside of the stage (it has a evenhandler for when it loses focus).
However, sometimes I NEED the user to select one or more cards from the deck using this window. I do not want to allow him to close the window until he has selected at least one card. This means I had to use MODAL to stop him from being able to access the stage underneath (My Applicaiton). The problem with MODAL is now he can never leave the window like he could before by clicking outside the stage, even when I want him to be able to. He is now only able to leave through right clicking. I could add a button but I'd really rather not.
I hope I explained my problem well enough. What would you guys recommend I do? Is there a way I could somehow block the user from going back to the previous stage without MODAL? I'm also not able to change Modality after the Stage has been shown, so that won't work.
Thanks!
The idea is to use the onCloseRequestProperty property of your pop-up Stage.
Called when there is an external request to close this Window. The
installed event handler can prevent window closing by consuming the
received event.
With this property you can interrupt the closing of the Stage if a condition (in your case at lest one card is selected) is not met by calling consume on the WindowEvent.
Note: As the documentation states: it is only valid if the request is external, so if you call the close method of the Stage, the attached listener will be not executed. As a solution rather than calling this method you can fire the WindowEvent.WINDOW_CLOSE_REQUEST event manually.
Example:
public class PopUpApp extends Application {
Stage popupStage;
Stage primaryStage;
#Override
public void start(Stage stage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
primaryStage = stage;
initPopUpStage();
// When the Pop-Up stage is showing, do not handle any action on the
// main GUI
root.disableProperty().bind(popupStage.showingProperty());
Button b = new Button("Open deck");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// Add some ToggleButtons to simulate the cards
VBox vbox = new VBox();
vbox.setAlignment(Pos.CENTER);
List<ToggleButton> toggles = new ArrayList<ToggleButton>();
for (int i = 0; i < 4; i++) {
ToggleButton tb = new ToggleButton("Card " + i + 1);
toggles.add(tb);
}
vbox.getChildren().addAll(toggles);
Scene sc = new Scene(vbox, 300, 300);
popupStage.setScene(sc);
// On close request check for the condition
popupStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
Boolean readytoClose = false;
for (ToggleButton toggle : toggles) {
if (toggle.isSelected()) {
readytoClose = true;
break;
}
}
// Consume the event a show a dialog
if (!readytoClose) {
event.consume();
Alert alert = new Alert(AlertType.INFORMATION,
"At least one card has be to be selected!");
alert.showAndWait();
}
}
});
popupStage.show();
}
});
root.setCenter(b);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
private void initPopUpStage() {
popupStage = new Stage();
popupStage.initOwner(primaryStage);
popupStage.initStyle(StageStyle.UNDECORATED);
// On focus loss, close the window
popupStage.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
// Rather than popupStage.close(); fire the event manually
if (!newValue)
popupStage.fireEvent(new WindowEvent(popupStage, WindowEvent.WINDOW_CLOSE_REQUEST));
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Update:
To make the main Stage unavailable I have added this line:
root.disableProperty().bind(popupStage.showingProperty());
This will disable the root BorderPane while the pop-up stage is showing. As soon as the pop-up window closed, the main window is enabled again.
Basically, I have a okayButton that sits in a stage and when it is clicked , it performs a list of tasks. Now I want to bind the Enter key to this button such that when it is clicked OR the ENTER key is pressed, it performs a list of tasks.
okayButton.setOnAction(e -> {
.........
}
});
How can I do that ? I have read the following post already. However, it did not help me to achieve what I want to do.
First, set a hanlder on your button :
okayButton.setOnAction(e -> {
......
});
If the button has the focus, pressing Enter will automatically call this handler. Otherwise, you can do this in your start method :
#Override
public void start(Stage primaryStage) {
// ...
Node root = ...;
setGlobalEventHandler(root);
Scene scene = new Scene(root, 0, 0);
primaryStage.setScene(scene);
primaryStage.show();
}
private void setGlobalEventHandler(Node root) {
root.addEventHandler(KeyEvent.KEY_PRESSED, ev -> {
if (ev.getCode() == KeyCode.ENTER) {
okayButton.fire();
ev.consume();
}
});
}
If you have only one button of this kind, you can use the following method instead.
okayButton.setDefaultButton(true);
You can dynamically change the default button property of the currently focused button by using binding
btn.defaultButtonProperty().bind(btn.focusedProperty());
I've had the same problem like mynameisJEFF. (I'm using Windows and as I read here: http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-June/019234.html it is the SPACE_BAR and not ENTER, which fires a Button in JavaFX) I didn't want to add a listener to every Button, so I registered a Listener to the root node and asked the scene, which node is focused to fire that one. Here is my code (it is xtend, but I think it very easy to understand):
override start(Stage primaryStage) throws Exception {
val root = FXTable.createRoot
val mainScene = new Scene(root)
root.addEventHandler(KeyEvent.KEY_RELEASED, [event|
if(event.code === KeyCode.ENTER){
switch(focusedNode : mainScene.focusOwnerProperty.get){
Button:{
focusedNode.fire
event.consume
}
default:{
}
}
}
])
primaryStage.scene = mainScene
primaryStage.show
primaryStage.maximized = true
}
There is a much more simple a standard way to do that using setOnKeyPressed
okayButton.setOnKeyPressed(event -> {
if (event.getCode().equals(KeyCode.ENTER)) {
okayButton.fire();
}
}
);
And don't forget that you should define SetOnAction too, other way it's work but it's doing nothing.
okayButton.setOnAction(event -> {
// Do what ever you want to your button do. Like :
System.Out.Print("Okay Button Fired (Clicked or Pressed");
}
);
This should work:
okayButton.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
if(evt.getKeyCode() == KeyEvent.VK_ENTER){
System.out.print("Your function call or code can go here");
}
}
});
In JavaFX 2 I have a TableView beeing populated by reading an Excel file. It looks like this:
identification cellcount calved
o0001 12345 false
o0002 65432 true
o0003 55555 false
...
When users press the 'Import' button, all records have to be added to a database. However, If the 'calved' field has 'true' as value, I show a Dialog window where the users have to select a date to specify when the calving happened. Now the big question is that I want my for loop beeing paused as soon as a Dialog window is open. With my current code, all Dialog windows are stacked on eachother.
This is the Dialog method which loads an FXML:
public void showDialog(String sURL){
final Stage myDialog = new Stage();
myDialog.initStyle(StageStyle.UTILITY);
myDialog.initModality(Modality.APPLICATION_MODAL);
URL url = getClass().getResource(sURL);
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setLocation(url);
fxmlloader.setBuilderFactory(new JavaFXBuilderFactory());
try {
Node n = (Node) fxmlloader.load(url.openStream());
Scene myDialogScene = new Scene(VBoxBuilder.create().children(n).alignment(Pos.CENTER).padding(new Insets(0)).build());
myDialog.setScene(myDialogScene);
myDialog.show();
} catch (Exception ex) {
System.out.println(ex);
}
}
And here is the for loop where I handle the tablerows:
#FXML
private void handle_ImportCowDataButton(ActionEvent event) {
Cows selectedCow;
for(ImportRow row: tblImport.getItems()){
selectedCow = null;
for (Cows cow : olCows) {
if (cow.getOfficial().equals(row.getCownumber())) {
selectedCow = cow;
}
}
if (selectedCow != null) {
if (row.getCalving()) {
//if cow exists and cow has calved, show dialog window loading addcalving.fxml
//then the for loop should wait until that dialog window is closed before continuing
Context.getInstance().setPassthroughObject(selectedCow);
Context.getInstance().showDialog("/GUI/calving/AddCalving.fxml");
}
} else {
//if cow does not exist, show dialog window loading addcow.fxml
//then the for loop should wait until that dialog window is closed before continuing
Context.getInstance().setPassthroughObject(selectedFarmer);
Context.getInstance().showDialog("/GUI/cow/AddCow.fxml");
}
}
}
Is working with setOnCloseRequest() in my showDialog() method an option?
If you copy the cows list into another data structure such as a queue and remove each cow as it is processed, it is relatively easy to resume processing it since only the cows that need to be processed remain.
It seems the answer was much easier then I thought, simply use the showAndWait() method instead of show(). How on earth could I have missed that... Thanks for the help thou.
Final code of the showDialog() method:
public void showDialog(String sURL){
final Stage myDialog = new Stage();
myDialog.initStyle(StageStyle.UTILITY);
myDialog.initModality(Modality.APPLICATION_MODAL);
URL url = getClass().getResource(sURL);
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setLocation(url);
fxmlloader.setBuilderFactory(new JavaFXBuilderFactory());
try {
Node n = (Node) fxmlloader.load(url.openStream());
Scene myDialogScene = new Scene(VBoxBuilder.create().children(n).alignment(Pos.CENTER).padding(new Insets(0)).build());
myDialog.setScene(myDialogScene);
myDialog.showAndWait();
} catch (Exception ex) {
System.out.println(ex);
}
}