0

I have a java application with old vector graphics editor written in pure Java Swing and Java2D.

Now I want to migrate the whole application to Javafx toolkit, except graphics editor. Graphics editor remains Swing/Java2D - based to avoid long rewriting of graphical event handling to Javafx. The only way i know to do this is the use of JavaFX SwingNode component.

The problem is with mouse painting on Swing component located inside JavaFX SwingNode: mouse events are handling well, but there is no graphical changes visible. The same component located in Swing JFrame is painting well. (Note that painting MUST be performed without full repaint of the swing component).

Does anyone know any way to achieve the same behavour in SwingNode as in JFrame?

The test class TestPaintOnSwingNode.java below do the work:

package x;

import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

public class TestPaintOnSwingNode extends Application {
    private Scene scene;
    private Stage stage;

    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("Swing PaintPanel on JavaFX Stage with SwingNode");
        stage.setWidth(1200);
        stage.setHeight(800);
        AnchorPane rootPane = new AnchorPane();
        scene = new Scene(rootPane);

        createPaintPaneOnFXSwingNode(rootPane);
        SwingUtilities.invokeLater(()->
            createPaintPaneOnJFrame()
        );
        stage.setScene(scene);
        stage.show();
    }

    private void createPaintPaneOnJFrame() {
        JFrame frame = new JFrame();
        frame.setTitle("Swing PaintPanel on Swing JFrame");
        frame.setSize(new Dimension(1200, 800));
        frame.add(new PaintPanel());
        frame.setVisible(true);

    }

    private void createPaintPaneOnFXSwingNode(AnchorPane pane) {
        SwingNode swingNode = new SwingNode();
        SwingUtilities.invokeLater(() -> swingNode.setContent(
                new PaintPanel()
        ));
        pane.getChildren().add(swingNode);
        AnchorPane.setLeftAnchor(swingNode, 0D);
        AnchorPane.setRightAnchor(swingNode, 0D);
        AnchorPane.setTopAnchor(swingNode, 0D);
        AnchorPane.setBottomAnchor(swingNode, 0D);
    }
}

class PaintPanel extends JPanel implements MouseListener, MouseMotionListener {

    private int lastX = 0;
    private int lastY = 0;

    PaintPanel() {
        addMouseListener(this);
        addMouseMotionListener(this);
        setBackground(Color.ORANGE);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        lastX = e.getX();
        lastY = e.getY();
    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

    @Override
    public void mouseDragged(MouseEvent e) {

    }

    @Override
    public void mouseMoved(MouseEvent e) {
        if (lastX > 0) {
            System.out.println(e);
            Graphics2D graphics = (Graphics2D) getGraphics();
            graphics.drawLine(lastX, lastY, e.getX(), e.getY());
            lastX = e.getX();
            lastY = e.getY();
        }
    }
}

Results after running the test class and some mouse motion over a component are shown below. The test simply draws a line from previuos mouse coordinate to current. Painting starts after first click on a PaintPanel. Events are printing well in console when mouse is moved on each of both dialogs. But painting results are visible only in native Swing window.

3
  • 2
    Do not call getGraphics(). That is not how Swing custom painting works. Instead, you need to override paintComponent and make sure the first line of code in the method body is super.paintComponent. See docs.oracle.com/javase/tutorial/uiswing/painting. Commented Jul 15, 2021 at 13:16
  • If override paintComponent, there should be an algorithm to draw a component completely. Otherwise paintComponent will clear results of a previous drawing, e.g. prevoius mouse motions. This is important for an event handling in vector graphics editor! Is there any other way to trigger a repaint without clearing all previously painted graphics? Commented Jul 15, 2021 at 15:51
  • You don’t have control over which parts of your window get erased. The user could put another window in front of it. Or iconify it. Or lock the screen. All of these are why your paintComponent method must be prepared to paint the entire panel. Try defining a private Path2D field instead of just a pair of coordinates. Commented Jul 20, 2021 at 1:15

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.