14

I am currently evaluating whether it would be feasible to convert my current Swing Application to JavaFX 8 using SwingNode and then slowly change everything to JavaFX components. I have stumbled over two problems, which may be lingering bugs in JavaFX 8, but I can't be sure.

  1. After adding content to the SwingNode the Javafx menu isn't painted properly (most of the time), resulting in a black bar
  2. Making JScrollBars non-opaque doesn't seem to work

    import java.awt.Color;
    import java.awt.GridLayout;
    
    import javafx.application.Application;
    import javafx.embed.swing.SwingNode;
    import javafx.scene.Scene;
    import javafx.scene.control.Menu;
    import javafx.scene.control.MenuBar;
    import javafx.scene.layout.StackPane;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    
    
    public class Test extends Application {    
    
        @Override
        public void start(Stage stage) throws Exception {
            stage.setMaximized(true);
    
            VBox vbox = new VBox();
            MenuBar menuBar = new MenuBar();
            menuBar.getMenus().add(new Menu("bla"));
            vbox.getChildren().add(menuBar);
    
            StackPane mainPane = new StackPane();        
            mainPane.setPrefSize(9999, 9999); //Better way to ensure all space used?
            vbox.getChildren().add(mainPane);
            vbox.setVisible(true);
    
            Scene scene = new Scene(vbox, 9999, 9999); //Better way to ensure all space used?
            stage.setScene(scene);
    
            SwingNode swingNode = new SwingNode();
            mainPane.getChildren().add(swingNode);
    
            JPanel jPanel = new JPanel();
            swingNode.setContent(jPanel); //Seems to interfere with painting of menu
    
            jPanel.setBackground(Color.WHITE);
            jPanel.setLayout(new GridLayout(2, 1));
            jPanel.add(new JPanel());
    
            JPanel nonOpaquePanel = new JPanel();
            nonOpaquePanel.setOpaque(false);
            JScrollPane scrollPane = new JScrollPane(nonOpaquePanel);
            scrollPane.setOpaque(false);
            scrollPane.getViewport().setOpaque(false); //Black background -> Bug?
            jPanel.add(scrollPane);
    
            stage.show();
        }
    
        /**
         * Main method launching the application.
         */
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    Executable jar

Edit: I edited my example to use the EDT for creating the Swing components. That didn't change anything. Or am I doing something wrong?

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;


public class Test extends Application {
    private JPanel jPanel = null;

    @Override
    public void start(Stage stage) throws Exception {
        if (Platform.isFxApplicationThread()) { // all of this happens on the fx application thread
            stage.setMaximized(true);

            VBox vbox = new VBox();
            MenuBar menuBar = new MenuBar();
            menuBar.getMenus().add(new Menu("bla"));
            vbox.getChildren().add(menuBar);

            StackPane mainPane = new StackPane();        
            mainPane.setPrefSize(9999, 9999); //Better way to ensure all space used?
            vbox.getChildren().add(mainPane);
            vbox.setVisible(true);

            Scene scene = new Scene(vbox, 9999, 9999); //Better way to ensure all space used?
            stage.setScene(scene);

            SwingNode swingNode = new SwingNode();
            mainPane.getChildren().add(swingNode);                    

            SwingUtilities.invokeLater(new Runnable() { //Use EDT to build swing components              
                @Override
                public void run() {
                    jPanel = new JPanel();

                    jPanel.setBackground(Color.WHITE);
                    jPanel.setLayout(new GridLayout(2, 1));
                    jPanel.add(new JPanel());

                    JLabel nonOpaquePanel = new JLabel("Bla");
                    nonOpaquePanel.setPreferredSize(new Dimension(5000, 5000));
                    nonOpaquePanel.setOpaque(false);
                    JScrollPane scrollPane = new JScrollPane(nonOpaquePanel);
                    scrollPane.setOpaque(false);
                    scrollPane.getViewport().setOpaque(false); //Black background -> Bug?
                    jPanel.add(scrollPane);
                }
            });

            Thread.sleep(1000); //terrible way to wait for the EDT, I know
            swingNode.setContent(jPanel); //Seems to interfere with painting of menu            
            stage.show();

            SwingUtilities.invokeLater(new Runnable() { //I am pretty sure this isn't necessary, but whatever                       
                @Override
                public void run() {
                    jPanel.repaint();    
                }
            });
        }
    }

    /**
     * Main method launching the application.
     */
    public static void main(String[] args) {
        launch(args);
    }
}
Teferus
  • 151
  • 1
  • 7
  • 3
    I asked about this question in JavaFX bug report RT-36285, and heard: "... please check the threading in your code. The sample that you provide on stackoverflow calls Swing APIs on JavaFX User Thread. This is bad for both Swing and FX. Swing APIs should only be invoked on the EDT, and FX APIs - on the JavaFX User Thread correspondingly. Please fix threading first and see how it changes things. Then file new bugs if any issues are still remaining.". – Rob I Apr 15 '14 at 16:59
  • You don’t set the `JScrollBar`s to non-opaque but the entire `JScrollPane`. However, both won’t work. It’s not quite clear what behavior you expect from a `JScrollPane` with non-opaque `JScrollBar`s but it’s very likely that it will not work that way (*in principle*). – Holger May 19 '14 at 09:19
  • When I compare your code with the [Tutorial from Oracle](http://docs.oracle.com/javase/8/javafx/interoperability-tutorial/embed-swing.htm), that it seems like, that you need to the call `swingNode.setContent(...)` into `SwingUtilities.invokeLater(...)` instead of the creation of the Swing content itself. Combined with the single-thread system property from @Dashy you should have given it you best. But currently your code looks wrong to me. – dzim Apr 21 '16 at 12:00

3 Answers3

4

Try wrapping your update code in this when you make an update call. This code has helped me before on other projects.

Platform.runLater(new Runnable() {
            @Override
            public void run() {
                // Update the text node with calculated results
            }
       });

Source

Community
  • 1
  • 1
Kyte
  • 834
  • 2
  • 12
  • 27
1

You can use -Djavafx.embed.singleThread=true, so you dont have to make the event dispatching on your own.

JavaFx APIDesign

Saif
  • 6,804
  • 8
  • 40
  • 61
Dashy
  • 61
  • 3
1

Must have been a bug in JavaFX, this very example is working fine now.

Teferus
  • 151
  • 1
  • 7