2
votes

I created a set of JavaFX components in a Scene which I'd like to display in a resizable modal dialog in a swing applications. The JavaFX components include ImageViews for scanned images which can get quite big dependening on the zoom level, so precise layouting in an issue. My options are afaik

  • displaying a JavaFX Dialog with showAndWait in a Platform.runLater and stop the Swing EDT with an invisible JDialog. That apparently causes deadlocks and is quite unelegant.
  • put the JavaFX components in a JFXPanel and display it in a JDialog. That works in terms of modality, but I have no idea how to layout components in the JFXPanel since in a GroupLayout the panel simply grows infinitely (JavaFX ScrollPane don't have any effect).

For example:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class NewMain1 extends Application {
    private final ImageView imageView;

    public NewMain1() {
        this.imageView = new ImageView(NewMain.class.getResource("/File_CC-BY-SA_3_icon_88x31.png").toString());
    }

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        BorderPane borderPane = new BorderPane();
        Button bottomButton = new Button("Some button");
        ScrollPane imageViewScrollPane = new ScrollPane(imageView);
        borderPane.setCenter(imageViewScrollPane);
        borderPane.setBottom(bottomButton);
        imageView.setSmooth(true);
        imageView.setFitHeight(400);
        StackPane  root  =  new  StackPane();
        root.getChildren().add(borderPane);
        stage.setScene(new Scene(root, 800, 600));
        stage.show();
    }
}

shows a well working ScrollPane for the ImageView whereas in a JFXPanel in a JDialog the scrolling/layout doesn't work:

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.HeadlessException;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

public class NewMain extends JFrame {
    private static final long serialVersionUID = 1L;
    private final JFXPanel mainPanel = new JFXPanel();
    private final ImageView imageView;
    private final JButton closeButton = new JButton("Close");

    public NewMain() throws HeadlessException {
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setBounds(0, 0, 800, 600);
        setPreferredSize(new Dimension(800, 600));
        GroupLayout layout = new GroupLayout(this.getContentPane());
        this.getContentPane().setLayout(layout);
        layout.setAutoCreateContainerGaps(true);
        layout.setAutoCreateGaps(true);
        this.imageView = new ImageView(NewMain.class.getResource("/File_CC-BY-SA_3_icon_88x31.png").toString());
        Platform.runLater(() -> {
            BorderPane borderPane = new BorderPane();
            Button bottomButton = new Button("Some button");
            ScrollPane imageViewScrollPane = new ScrollPane(imageView);
            borderPane.setCenter(imageViewScrollPane);
            borderPane.setBottom(bottomButton);
            imageView.setSmooth(true);
            imageView.setFitHeight(400);
            Group  root  =  new  Group();
            Scene  scene  =  new  Scene(root, Color.ALICEBLUE);
            root.getChildren().add(borderPane);
            mainPanel.setScene(scene);
        });
        closeButton.addActionListener((event) -> {
            setVisible(false);
        });
        layout.setHorizontalGroup(layout.createParallelGroup()
                .addComponent(mainPanel)
                .addComponent(closeButton));
        layout.setVerticalGroup(layout.createSequentialGroup()
                .addComponent(mainPanel)
                .addComponent(closeButton));
        pack();
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            new NewMain().setVisible(true);
        });
    }
}

The example is available at https://github.com/krichter722/javafx-in-jdialog-in-swing.

1

1 Answers

2
votes

Don't make the root of the Scene a Group, Groups are not resizable.

Just remove the group and use a resizable layout for the scene root (you already have a resizable layout in your sample code, it's the BorderPane, so you can just use that).

Instead of:

Group  root  =  new  Group();
Scene  scene  =  new  Scene(root, Color.ALICEBLUE);
root.getChildren().add(borderPane);

Write:

Scene  scene  =  new  Scene(borderPane, Color.ALICEBLUE);

ScrollPane inside a JavaFX scene inside a JFXPanel inside a Swing JFrame.

snapshot

Note, that in your pure JavaFX NewMain1 application, you use are already using a resizable pane as a root (a StackPane), so that is the reason for the discrepancy that you observed between the pure JavaFX version and the Swing embedded version.