Making JavaFX Alerts/Dialogs Modal within Swing Application
Asked Answered
S

3

6

So once again we are in the process of converting our existing Java application that was using entirely Swing to using JavaFX. However, the application will not be using JavaFX entirely. This seems to be causing some issues with Alerts/Dialogs and modality. We are currently using Java 8u40.

The main application is basically in a JFrame that has a Menu. The main content pane is JDesktopPane and clicking a MenuItem opens new JInternalFrames within the DeskopPane. Screens we are converting to JavaFX are basically JFXPanels within a JInternalFrame at the moment. Any Alerts/Dialogs that are opened from the JFXPanels are modal to the panel itself, but not to the JInternalFrame, DeskopPane, Menu, etc.

I read in the DialogPane documentation that they are planning to introduce some lightweight dialogs and even possibly InternalFrames in future releases of JavaFX, so maybe we'll just have to wait it out a little longer for this functionality. But, ideally when opening a new Alert/Dialog it would be modal to the entire Application.

EDIT: Currently doing the following for modal dialogs:

((Stage)getDialogPane().getScene().getWindow()).setAlwaysOnTop(true);

This makes the dialog always appear on top, however the dialog also remains on top of other applications even if our main application is minimized. It also does not block input to any Swing components in the frame.

Schoolgirl answered 21/1, 2015 at 1:28 Comment(9)
What exactly is your question?Arianaariane
How do I make the JavaFX dialogs modal to my entire Swing Application?Schoolgirl
How do you plan to port the JDesktopPane with its internal frames to FX? It seems there is no counterpart there. Maybe you will use the 'Docking framework'? According to Jira this is still unresolved (javafx-jira.kenai.com/browse/RT-14039). Do you got any code we can discuss specifically?Arianaariane
I guess that's part of the problem and why the application won't be completely JavaFX and will still ultimately be embedded in a Swing application.Schoolgirl
I don't know your application at all but maybe you could think about a new usage concept - maybe with accordions, tabs, splitpanes or whatever. I just won't believe that your needs can only covered with swing and not with FX.Arianaariane
care to share some part of your code that i can look at so as to try to write some solution, so basically, you want your dialog to respect your entire activity state right?Jeraldjeraldine
Yes, ultimately I would like the dialogs to be modal to both Swing components and FX Nodes, while not remaining on top of other applications.Schoolgirl
AFAIK there is simply no way to make a JavaFX window modal with respect to a Swing window. I think you need to stick to windows from one framework only: either JavaFX or Swing. (Probably you will use only Swing windows until you are ready to go "JavaFX only", and then switch to all JavaFX.) You can use JFXPanels to embed JavaFX content into a Swing JFrame or JDialog, or SwingNodes to embed swing content into a Stage or JavaFX Dialog. The threading might get a little ugly, but should be manageable.Headroom
Any suggestions for getting the JDialog to size appropriately? I was previously doing the following in the onShowingEvent: getDialogPane().getScene().getWindow().sizeToScene(); getDialogPane().getScene().getWindow().centerOnScreen();Schoolgirl
D
3

You can use the following work-around which creates an invisible JDialog when the Alert is shown and disposes the JDialog when the Alert is closed. This approach extends the modality to the whole application, including the Swing part.

// create Alert
Alert alert = new Alert(AlertType.INFORMATION, "Hello");

// create invisible JDialog and "show" it
JDialog dialog = new JDialog();
dialog.setModal(true);
dialog.setUndecorated(true);
dialog.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
SwingUtilities.invokeLater(() -> dialog.setVisible(true));

// show Alert
alert.showAndWait();

// close JDialog after Alert is closed
dialog.dispose();
Determination answered 6/11, 2019 at 12:47 Comment(0)
S
1

I don't think i understand your question completely. But here is my guess - You are trying to make an alert window from some JFXPanel that will be modal (i.e. user will not be able to click in your application until she closes that alert window) to your entire application which is written partially using swing components.

If your application would be written in purely JavaFX then you would do something like (Assuming you have created a button somewhere in your JFXPanel)

button.setOnAction(evt -> {
    Alert alert = new Alert(Alert.AlertType.INFORMATION);
    alert.initModality(Modality.APPLICATION_MODAL);

    // This will not work in your code
    alert.initOwner(button.getScene().getWindow());

    alert.show();
});

but since initOwner requires a javafx.stage.window object passing a swing component won't work in your code. As of Java 8u40 i don't think there is a right way(i.e. not hacks) to set ownership of Alert objects to swing component. Not surprisingly such questions has already been asked here and not answered as of writing this.

For your requirements you can use JOptionPane.showMessageDialog method and its look alike as workaround.

button.setOnAction(evt -> {
    JOptionPane.showMessageDialog(desktopPane,"My message");
});

These dialog boxes are modal by default so no work is necessary. You can call these from any event handler methods of JavaFX components.

Seraphim answered 8/9, 2015 at 13:39 Comment(2)
Thanks for the response and that is exactly what I was asking. Only problem with the JOptionPanes is that they would not have the same look and feel that the rest of the (mostly) JavaFX screens. I might need to rework existing code to just use a JDialog with a JFXPanel as James_D suggested above.Schoolgirl
@Schoolgirl I am afraid that currently that is the only way. But still you can set look and feel of swing components to somewhat close to JavaFX look and feel. It would be great if Oracle provide JavaFX look and feel for swing but neither Oracle nor some third party L&F are available.Seraphim
Q
1

I've done a little workaround with a small interface which is implemented in my JavaFXFrame:

public interface DialogParent {
    void setOnFocusGained(EventHandler<FocusEvent> focusHandler);
    void setOnCloseRequest(EventHandler<WindowEvent> closeHandler);
}

And my JavaFXFrame implementation

public class JavaFXFrame implements DialogParent {
    private JFrame frame;
    private EventHandler<ch.irix.sumadmin.util.FocusEvent> focusGainedHandler;
    private EventHandler<javafx.stage.WindowEvent> windowClosingHandler;

public void JavaFXFrame() {
    final JFXPanel fxPanel = new JFXPanel();
    frame = new JFrame();
    frame.add(fxPanel);
    frame.addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent e) {
            tryClosing(this);
        }
    });
    frame.addWindowFocusListener(new WindowAdapter() {
        @Override
        public void windowGainedFocus(WindowEvent e) {
            if (focusGainedHandler != null) {
                focusGainedHandler.handle(new FocusEvent());
            }
        }
    });
}

public void setVisible(boolean visible) {
    frame.setVisible(visible);
}

private void tryClosing(WindowListener listener) {
    javafx.stage.WindowEvent windowEvent = new javafx.stage.WindowEvent(null,    javafx.stage.WindowEvent.WINDOW_CLOSE_REQUEST);
    if (windowClosingHandler != null) {
        windowClosingHandler.handle(windowEvent);
    }
    if (!windowEvent.isConsumed()) {
        frame.setVisible(false);
    }
}

@Override
public void setOnFocusGained(EventHandler<ch.irix.sumadmin.util.FocusEvent> focusGainedHandler) {
    this.focusGainedHandler = focusGainedHandler;
}

@Override
public void setOnCloseRequest(EventHandler<javafx.stage.WindowEvent> windowClosingHandler) {
    this.windowClosingHandler = windowClosingHandler;
}

}

And showing an Alert:

public static void showAlert(Alert alert) {
    DialogPane dialogPane = alert.getDialogPane();
    final Stage stage = new Stage();
    stage.setScene(dialogPane.getScene());
    List<ButtonType> buttonTypes = dialogPane.getButtonTypes();
    for (ButtonType buttonType : buttonTypes) {
        ButtonBase button = (ButtonBase) dialogPane.lookupButton(buttonType);
        button.setOnAction(evt -> {
            dialogPane.setUserData(buttonType);
            stage.close();
        });
    }
    dialogParent.setOnFocusGained(event -> {
        stage.toFront();
    });
    dialogParent.setOnCloseRequest(Event::consume);
    stage.setOnCloseRequest(event -> {
        dialogParent.setOnFocusGained(null);
        dialogParent.setOnCloseRequest(null);
    });
    stage.show();
}

Hope this will help you

Queer answered 12/11, 2018 at 13:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.