This question is a follow-up question to Custom object drag-and-drop from FX to Swing.
I'm working on a plugin for a Swing application that uses JavaFX for some graphical user interfaces. We added drag-and-drop functionality to improve the user experience. First, we were using an external JavaFX window (Stage
) for our Scene
, now we want to embed it directly into the Swing application via a JFXPanel
.
Now, the strange thing is, that it seems to make a big difference for drag-and-drop whether the exactly same Scene
is loaded in a Stage
or in a JFXPanel
.
I already encountered some problems when trying to drag some custom Java object (in serialized form) with a custom MIME type from a JavaFX application into a Swing application. However, my problems were solved in the question I mentioned above. Now, using the embedded JavaFX application, I encounter some new problems, so I wanted to ask if someone had similar problems or knows a solution for this scenario.
I've written a MVCE, it's a simple Java application with a drag-supporting JFXPanel
on the one side and a drop-supporting JPanel
on the other side:
public class MyApp {
public static final DataFormat FORMAT = new DataFormat(
// this works fine in a separate window
//"JAVA_DATAFLAVOR:application/x-my-mime-type; class=java.lang.String",
"application/x-my-mime-type; class=java.lang.String");
public static final DataFlavor FLAVOR;
static {
try {
FLAVOR = new DataFlavor("application/x-my-mime-type; class=java.lang.String");
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
public static void main(String[] args) {
new MyApp().run();
}
private void run() {
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(1, 2));
frame.add(buildFX());
frame.add(buildSwing());
frame.setSize(300, 300);
frame.setVisible(true);
}
private JFXPanel buildFX() {
BorderPane parent = new BorderPane();
parent.setOnDragDetected(event -> {
Dragboard dragboard = parent.startDragAndDrop(TransferMode.COPY);
ClipboardContent content = new ClipboardContent();
content.put(FORMAT, "Test");
dragboard.setContent(content);
event.consume();
});
JFXPanel panel = new JFXPanel();
panel.setScene(new Scene(parent));
return panel;
}
@SuppressWarnings("serial")
private JPanel buildSwing() {
JPanel panel = new JPanel();
panel.setBackground(Color.ORANGE);
panel.setTransferHandler(new TransferHandler() {
@Override
public boolean canImport(TransferSupport support) {
return support.isDataFlavorSupported(FLAVOR);
}
@Override
public boolean importData(TransferSupport support) {
if (!canImport(support)) return false;
try {
String data = (String) support.getTransferable().getTransferData(FLAVOR);
System.out.println(data);
return true;
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
});
return panel;
}
}
According to the answer in the other question, using the prefix JAVA_DATAFLAVOR:
in the DataFormat
is necessary for Swing to handle the MIME type correctly. However, when using such a DataFormat
inside a JFXPanel
(disabled in the example), it seems like Java tries to construct a DataFlavor
when dragging from the FX application and fails to parse the MIME type with the prefix:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: failed to parse:JAVA_DATAFLAVOR:application/x-my-mime-type; class=java.lang.String
at java.awt.datatransfer.DataFlavor.<init>(Unknown Source)
at javafx.embed.swing.SwingDnD$DnDTransferable.getTransferDataFlavors(SwingDnD.java:394)
at sun.awt.datatransfer.DataTransferer.getFormatsForTransferable(Unknown Source)
at sun.awt.dnd.SunDragSourceContextPeer.startDrag(Unknown Source)
at java.awt.dnd.DragSource.startDrag(Unknown Source)
at java.awt.dnd.DragSource.startDrag(Unknown Source)
at java.awt.dnd.DragGestureEvent.startDrag(Unknown Source)
at javafx.embed.swing.SwingDnD.startDrag(SwingDnD.java:280)
at javafx.embed.swing.SwingDnD.lambda$null$66(SwingDnD.java:247)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Using only the pure MIME type, without the prefix, the drag-and-drop operation works and I can even receive the correct DataFlavor
(java.awt.datatransfer.DataFlavor[mimetype=application/x-my-mime-type;representationclass=java.lang.String]
), but the dropped data is always null
. As seen in the other question, using this second approach with two separated windows, I can't even receive the DataFlavor
, but now it works somehow to this limited point.