60
61 import javafx.application.Platform;
62 import javafx.scene.Scene;
63
64 import javax.swing.JComponent;
65 import javax.swing.SwingUtilities;
66
67 import com.sun.javafx.application.PlatformImpl;
68 import com.sun.javafx.cursor.CursorFrame;
69 import com.sun.javafx.embed.AbstractEvents;
70 import com.sun.javafx.embed.EmbeddedSceneInterface;
71 import com.sun.javafx.embed.EmbeddedStageInterface;
72 import com.sun.javafx.embed.HostInterface;
73 import com.sun.javafx.stage.EmbeddedWindow;
74 import com.sun.javafx.tk.Toolkit;
75 import com.sun.javafx.PlatformUtil;
76 import java.awt.event.InvocationEvent;
77
78 import java.lang.reflect.Method;
79 import java.util.concurrent.atomic.AtomicInteger;
80 import sun.awt.AppContext;
81 import sun.awt.SunToolkit;
82 import sun.java2d.SunGraphics2D;
83 import sun.java2d.SurfaceData;
84 import com.sun.javafx.logging.PlatformLogger;
85 import com.sun.javafx.logging.PlatformLogger.Level;
86
87 /**
88 * {@code JFXPanel} is a component to embed JavaFX content into
89 * Swing applications. The content to be displayed is specified
90 * with the {@link #setScene} method that accepts an instance of
91 * JavaFX {@code Scene}. After the scene is assigned, it gets
92 * repainted automatically. All the input and focus events are
93 * forwarded to the scene transparently to the developer.
94 * <p>
95 * There are some restrictions related to {@code JFXPanel}. As a
96 * Swing component, it should only be accessed from the event
97 * dispatch thread, except the {@link #setScene} method, which can
98 * be called either on the event dispatch thread or on the JavaFX
99 * application thread.
100 * <p>
101 * Here is a typical pattern how {@code JFXPanel} can used:
102 * <pre>
103 * public class Test {
104 *
105 * private static void initAndShowGUI() {
106 * // This method is invoked on Swing thread
433
434 /**
435 * Overrides the {@link java.awt.Component#processMouseEvent(MouseEvent)}
436 * method to dispatch the mouse event to the JavaFX scene attached to this
437 * {@code JFXPanel}.
438 *
439 * @param e the mouse event to dispatch to the JavaFX scene
440 */
441 @Override
442 protected void processMouseEvent(MouseEvent e) {
443 if ((e.getID() == MouseEvent.MOUSE_PRESSED) &&
444 (e.getButton() == MouseEvent.BUTTON1)) {
445 if (!hasFocus()) {
446 requestFocus();
447 // this focus request event goes to eventqueue and will be
448 // asynchronously handled so MOUSE_PRESSED event will not be
449 // honoured by FX immediately due to lack of focus in fx
450 // component. Fire the same MOUSE_PRESSED event after
451 // requestFocus() so that 2nd mouse press will be honoured
452 // since now fx have focus
453 AppContext context = SunToolkit.targetToAppContext(this);
454 if (context != null) {
455 SunToolkit.postEvent(context, e);
456 }
457 }
458 }
459
460 sendMouseEventToFX(e);
461 super.processMouseEvent(e);
462 }
463
464 /**
465 * Overrides the {@link java.awt.Component#processMouseMotionEvent(MouseEvent)}
466 * method to dispatch the mouse motion event to the JavaFX scene attached to
467 * this {@code JFXPanel}.
468 *
469 * @param e the mouse motion event to dispatch to the JavaFX scene
470 */
471 @Override
472 protected void processMouseMotionEvent(MouseEvent e) {
473 sendMouseEventToFX(e);
474 super.processMouseMotionEvent(e);
475 }
476
553 }
554 super.processComponentEvent(e);
555 }
556
557 // called on EDT only
558 private void updateComponentSize() {
559 int oldWidth = pWidth;
560 int oldHeight = pHeight;
561 // It's quite possible to get negative values here, this is not
562 // what JavaFX embedded scenes/stages are ready to
563 pWidth = Math.max(0, getWidth());
564 pHeight = Math.max(0, getHeight());
565 if (getBorder() != null) {
566 Insets i = getBorder().getBorderInsets(this);
567 pWidth -= (i.left + i.right);
568 pHeight -= (i.top + i.bottom);
569 }
570 double newScaleFactorX = scaleFactorX;
571 double newScaleFactorY = scaleFactorY;
572 Graphics g = getGraphics();
573 if (g instanceof SunGraphics2D) {
574 SurfaceData sd = ((SunGraphics2D) g).surfaceData;
575 newScaleFactorX = sd.getDefaultScaleX();
576 newScaleFactorY = sd.getDefaultScaleY();
577 }
578 if (oldWidth != pWidth || oldHeight != pHeight ||
579 newScaleFactorX != scaleFactorX || newScaleFactorY != scaleFactorY)
580 {
581 createResizePixelBuffer(newScaleFactorX, newScaleFactorY);
582 if (scenePeer != null) {
583 scenePeer.setPixelScaleFactors((float) newScaleFactorX,
584 (float) newScaleFactorY);
585 }
586 scaleFactorX = newScaleFactorX;
587 scaleFactorY = newScaleFactorY;
588 sendResizeEventToFX();
589 }
590 }
591
592 // This methods should only be called on EDT
593 private boolean updateScreenLocation() {
594 synchronized (getTreeLock()) {
595 if (isShowing()) {
596 Point p = getLocationOnScreen();
597 screenX = p.x;
748 if (!scenePeer.getPixels(buf, pWidth, pHeight)) {
749 // In this case we just render what we have so far in the buffer.
750 }
751
752 Graphics gg = null;
753 try {
754 gg = g.create();
755 if ((opacity < 1.0f) && (gg instanceof Graphics2D)) {
756 Graphics2D g2d = (Graphics2D)gg;
757 AlphaComposite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
758 g2d.setComposite(c);
759 }
760 if (getBorder() != null) {
761 Insets i = getBorder().getBorderInsets(this);
762 gg.translate(i.left, i.top);
763 }
764 gg.drawImage(pixelsIm, 0, 0, pWidth, pHeight, null);
765
766 double newScaleFactorX = scaleFactorX;
767 double newScaleFactorY = scaleFactorY;
768 if (g instanceof SunGraphics2D) {
769 SurfaceData sd = ((SunGraphics2D)g).surfaceData;
770 newScaleFactorX = sd.getDefaultScaleX();
771 newScaleFactorY = sd.getDefaultScaleY();
772 }
773 if (scaleFactorX != newScaleFactorX || scaleFactorY != newScaleFactorY) {
774 createResizePixelBuffer(newScaleFactorX, newScaleFactorY);
775 // The scene will request repaint.
776 scenePeer.setPixelScaleFactors((float) newScaleFactorX,
777 (float) newScaleFactorY);
778 scaleFactorX = newScaleFactorX;
779 scaleFactorY = newScaleFactorY;
780 }
781 } catch (Throwable th) {
782 th.printStackTrace();
783 } finally {
784 if (gg != null) {
785 gg.dispose();
786 }
787 }
788 }
789
790 /**
791 * Returns the preferred size of this {@code JFXPanel}, either
792 * previously set with {@link #setPreferredSize(Dimension)} or
811 if (!enabled) {
812 if (disableCount.incrementAndGet() == 1) {
813 if (dnd != null) {
814 dnd.removeNotify();
815 }
816 }
817 } else {
818 if (disableCount.get() == 0) {
819 //should report a warning about an extra enable call ?
820 return;
821 }
822 if (disableCount.decrementAndGet() == 0) {
823 if (dnd != null) {
824 dnd.addNotify();
825 }
826 }
827 }
828 }
829
830 private transient AWTEventListener ungrabListener = event -> {
831 if (event instanceof sun.awt.UngrabEvent) {
832 SwingFXUtils.runOnFxThread(() -> {
833 if (JFXPanel.this.stagePeer != null &&
834 getScene() != null &&
835 getScene().getFocusOwner() != null &&
836 getScene().getFocusOwner().isFocused()) {
837 JFXPanel.this.stagePeer.focusUngrab();
838 }
839 });
840 }
841 if (event instanceof MouseEvent) {
842 // Synthesize FOCUS_UNGRAB if user clicks the AWT top-level window
843 // that contains the JFXPanel.
844 if (event.getID() == MouseEvent.MOUSE_PRESSED && event.getSource() instanceof Component) {
845 final Window jfxPanelWindow = SwingUtilities.getWindowAncestor(JFXPanel.this);
846 final Component source = (Component)event.getSource();
847 final Window eventWindow = source instanceof Window ? (Window)source : SwingUtilities.getWindowAncestor(source);
848
849 if (jfxPanelWindow == eventWindow) {
850 SwingFXUtils.runOnFxThread(() -> {
851 if (JFXPanel.this.stagePeer != null) {
860 }
861 });
862 }
863 }
864 }
865 };
866
867 /**
868 * Notifies this component that it now has a parent component. When this
869 * method is invoked, the chain of parent components is set up with
870 * KeyboardAction event listeners.
871 */
872 @Override
873 public void addNotify() {
874 super.addNotify();
875
876 registerFinishListener();
877
878 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
879 JFXPanel.this.getToolkit().addAWTEventListener(ungrabListener,
880 SunToolkit.GRAB_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
881 return null;
882 });
883 updateComponentSize(); // see RT-23603
884 SwingFXUtils.runOnFxThread(() -> {
885 if ((stage != null) && !stage.isShowing()) {
886 stage.show();
887 sendMoveEventToFX();
888 }
889 });
890 }
891
892 @Override
893 public InputMethodRequests getInputMethodRequests() {
894 EmbeddedSceneInterface scene = scenePeer;
895 if (scene == null) {
896 return null;
897 }
898 return new InputMethodSupport.InputMethodRequestsAdapter(scene.getInputMethodRequests());
899 }
900
911 });
912
913 pixelsIm = null;
914 pWidth = 0;
915 pHeight = 0;
916
917 super.removeNotify();
918
919 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
920 JFXPanel.this.getToolkit().removeAWTEventListener(ungrabListener);
921 return null;
922 });
923
924 /* see CR 4867453 */
925 getInputContext().removeNotify(this);
926
927 deregisterFinishListener();
928 }
929
930 private void invokeOnClientEDT(Runnable r) {
931 AppContext context = SunToolkit.targetToAppContext(this);
932 if (context == null) {
933 if (log.isLoggable(Level.FINE)) log.fine("null AppContext encountered!");
934 return;
935 }
936 SunToolkit.postEvent(context, new InvocationEvent(this, r));
937 }
938
939 private class HostContainer implements HostInterface {
940
941 @Override
942 public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) {
943 stagePeer = embeddedStage;
944 if (stagePeer == null) {
945 return;
946 }
947 if (pWidth > 0 && pHeight > 0) {
948 stagePeer.setSize(pWidth, pHeight);
949 }
950 invokeOnClientEDT(() -> {
951 if (stagePeer != null && JFXPanel.this.isFocusOwner()) {
952 stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED);
953 }
954 });
955 sendMoveEventToFX();
956 }
1037 return cachedPlatformCursor;
1038 }
1039
1040 // platform cursor not cached yet
1041 final Cursor platformCursor =
1042 SwingCursors.embedCursorToCursor(cursorFrame);
1043 cursorFrame.setPlatforCursor(Cursor.class, platformCursor);
1044
1045 return platformCursor;
1046 }
1047
1048 @Override
1049 public boolean grabFocus() {
1050 // On X11 grab is limited to a single XDisplay connection,
1051 // so we can't delegate it to another GUI toolkit.
1052 if (PlatformUtil.isLinux()) return true;
1053
1054 invokeOnClientEDT(() -> {
1055 Window window = SwingUtilities.getWindowAncestor(JFXPanel.this);
1056 if (window != null) {
1057 if (JFXPanel.this.getToolkit() instanceof SunToolkit) {
1058 ((SunToolkit)JFXPanel.this.getToolkit()).grab(window);
1059 }
1060 }
1061 });
1062
1063 return true; // Oh, well...
1064 }
1065
1066 @Override
1067 public void ungrabFocus() {
1068 // On X11 grab is limited to a single XDisplay connection,
1069 // so we can't delegate it to another GUI toolkit.
1070 if (PlatformUtil.isLinux()) return;
1071
1072 invokeOnClientEDT(() -> {
1073 Window window = SwingUtilities.getWindowAncestor(JFXPanel.this);
1074 if (window != null) {
1075 if (JFXPanel.this.getToolkit() instanceof SunToolkit) {
1076 ((SunToolkit)JFXPanel.this.getToolkit()).ungrab(window);
1077 }
1078 }
1079 });
1080 }
1081 }
1082 }
|
60
61 import javafx.application.Platform;
62 import javafx.scene.Scene;
63
64 import javax.swing.JComponent;
65 import javax.swing.SwingUtilities;
66
67 import com.sun.javafx.application.PlatformImpl;
68 import com.sun.javafx.cursor.CursorFrame;
69 import com.sun.javafx.embed.AbstractEvents;
70 import com.sun.javafx.embed.EmbeddedSceneInterface;
71 import com.sun.javafx.embed.EmbeddedStageInterface;
72 import com.sun.javafx.embed.HostInterface;
73 import com.sun.javafx.stage.EmbeddedWindow;
74 import com.sun.javafx.tk.Toolkit;
75 import com.sun.javafx.PlatformUtil;
76 import java.awt.event.InvocationEvent;
77
78 import java.lang.reflect.Method;
79 import java.util.concurrent.atomic.AtomicInteger;
80
81 import com.sun.javafx.logging.PlatformLogger;
82 import com.sun.javafx.logging.PlatformLogger.Level;
83
84 import jdk.swing.interop.SwingInterOpUtils;
85
86 /**
87 * {@code JFXPanel} is a component to embed JavaFX content into
88 * Swing applications. The content to be displayed is specified
89 * with the {@link #setScene} method that accepts an instance of
90 * JavaFX {@code Scene}. After the scene is assigned, it gets
91 * repainted automatically. All the input and focus events are
92 * forwarded to the scene transparently to the developer.
93 * <p>
94 * There are some restrictions related to {@code JFXPanel}. As a
95 * Swing component, it should only be accessed from the event
96 * dispatch thread, except the {@link #setScene} method, which can
97 * be called either on the event dispatch thread or on the JavaFX
98 * application thread.
99 * <p>
100 * Here is a typical pattern how {@code JFXPanel} can used:
101 * <pre>
102 * public class Test {
103 *
104 * private static void initAndShowGUI() {
105 * // This method is invoked on Swing thread
432
433 /**
434 * Overrides the {@link java.awt.Component#processMouseEvent(MouseEvent)}
435 * method to dispatch the mouse event to the JavaFX scene attached to this
436 * {@code JFXPanel}.
437 *
438 * @param e the mouse event to dispatch to the JavaFX scene
439 */
440 @Override
441 protected void processMouseEvent(MouseEvent e) {
442 if ((e.getID() == MouseEvent.MOUSE_PRESSED) &&
443 (e.getButton() == MouseEvent.BUTTON1)) {
444 if (!hasFocus()) {
445 requestFocus();
446 // this focus request event goes to eventqueue and will be
447 // asynchronously handled so MOUSE_PRESSED event will not be
448 // honoured by FX immediately due to lack of focus in fx
449 // component. Fire the same MOUSE_PRESSED event after
450 // requestFocus() so that 2nd mouse press will be honoured
451 // since now fx have focus
452 SwingInterOpUtils.postEvent(this, e);
453 }
454 }
455
456 sendMouseEventToFX(e);
457 super.processMouseEvent(e);
458 }
459
460 /**
461 * Overrides the {@link java.awt.Component#processMouseMotionEvent(MouseEvent)}
462 * method to dispatch the mouse motion event to the JavaFX scene attached to
463 * this {@code JFXPanel}.
464 *
465 * @param e the mouse motion event to dispatch to the JavaFX scene
466 */
467 @Override
468 protected void processMouseMotionEvent(MouseEvent e) {
469 sendMouseEventToFX(e);
470 super.processMouseMotionEvent(e);
471 }
472
549 }
550 super.processComponentEvent(e);
551 }
552
553 // called on EDT only
554 private void updateComponentSize() {
555 int oldWidth = pWidth;
556 int oldHeight = pHeight;
557 // It's quite possible to get negative values here, this is not
558 // what JavaFX embedded scenes/stages are ready to
559 pWidth = Math.max(0, getWidth());
560 pHeight = Math.max(0, getHeight());
561 if (getBorder() != null) {
562 Insets i = getBorder().getBorderInsets(this);
563 pWidth -= (i.left + i.right);
564 pHeight -= (i.top + i.bottom);
565 }
566 double newScaleFactorX = scaleFactorX;
567 double newScaleFactorY = scaleFactorY;
568 Graphics g = getGraphics();
569 newScaleFactorX = SwingInterOpUtils.getDefaultScaleX(g);
570 newScaleFactorY = SwingInterOpUtils.getDefaultScaleY(g);
571 if (oldWidth != pWidth || oldHeight != pHeight ||
572 newScaleFactorX != scaleFactorX || newScaleFactorY != scaleFactorY)
573 {
574 createResizePixelBuffer(newScaleFactorX, newScaleFactorY);
575 if (scenePeer != null) {
576 scenePeer.setPixelScaleFactors((float) newScaleFactorX,
577 (float) newScaleFactorY);
578 }
579 scaleFactorX = newScaleFactorX;
580 scaleFactorY = newScaleFactorY;
581 sendResizeEventToFX();
582 }
583 }
584
585 // This methods should only be called on EDT
586 private boolean updateScreenLocation() {
587 synchronized (getTreeLock()) {
588 if (isShowing()) {
589 Point p = getLocationOnScreen();
590 screenX = p.x;
741 if (!scenePeer.getPixels(buf, pWidth, pHeight)) {
742 // In this case we just render what we have so far in the buffer.
743 }
744
745 Graphics gg = null;
746 try {
747 gg = g.create();
748 if ((opacity < 1.0f) && (gg instanceof Graphics2D)) {
749 Graphics2D g2d = (Graphics2D)gg;
750 AlphaComposite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
751 g2d.setComposite(c);
752 }
753 if (getBorder() != null) {
754 Insets i = getBorder().getBorderInsets(this);
755 gg.translate(i.left, i.top);
756 }
757 gg.drawImage(pixelsIm, 0, 0, pWidth, pHeight, null);
758
759 double newScaleFactorX = scaleFactorX;
760 double newScaleFactorY = scaleFactorY;
761 newScaleFactorX = SwingInterOpUtils.getDefaultScaleX(g);
762 newScaleFactorY = SwingInterOpUtils.getDefaultScaleY(g);
763 if (scaleFactorX != newScaleFactorX || scaleFactorY != newScaleFactorY) {
764 createResizePixelBuffer(newScaleFactorX, newScaleFactorY);
765 // The scene will request repaint.
766 scenePeer.setPixelScaleFactors((float) newScaleFactorX,
767 (float) newScaleFactorY);
768 scaleFactorX = newScaleFactorX;
769 scaleFactorY = newScaleFactorY;
770 }
771 } catch (Throwable th) {
772 th.printStackTrace();
773 } finally {
774 if (gg != null) {
775 gg.dispose();
776 }
777 }
778 }
779
780 /**
781 * Returns the preferred size of this {@code JFXPanel}, either
782 * previously set with {@link #setPreferredSize(Dimension)} or
801 if (!enabled) {
802 if (disableCount.incrementAndGet() == 1) {
803 if (dnd != null) {
804 dnd.removeNotify();
805 }
806 }
807 } else {
808 if (disableCount.get() == 0) {
809 //should report a warning about an extra enable call ?
810 return;
811 }
812 if (disableCount.decrementAndGet() == 0) {
813 if (dnd != null) {
814 dnd.addNotify();
815 }
816 }
817 }
818 }
819
820 private transient AWTEventListener ungrabListener = event -> {
821 if (SwingInterOpUtils.isUngrabEvent(event)) {
822 SwingFXUtils.runOnFxThread(() -> {
823 if (JFXPanel.this.stagePeer != null &&
824 getScene() != null &&
825 getScene().getFocusOwner() != null &&
826 getScene().getFocusOwner().isFocused()) {
827 JFXPanel.this.stagePeer.focusUngrab();
828 }
829 });
830 }
831 if (event instanceof MouseEvent) {
832 // Synthesize FOCUS_UNGRAB if user clicks the AWT top-level window
833 // that contains the JFXPanel.
834 if (event.getID() == MouseEvent.MOUSE_PRESSED && event.getSource() instanceof Component) {
835 final Window jfxPanelWindow = SwingUtilities.getWindowAncestor(JFXPanel.this);
836 final Component source = (Component)event.getSource();
837 final Window eventWindow = source instanceof Window ? (Window)source : SwingUtilities.getWindowAncestor(source);
838
839 if (jfxPanelWindow == eventWindow) {
840 SwingFXUtils.runOnFxThread(() -> {
841 if (JFXPanel.this.stagePeer != null) {
850 }
851 });
852 }
853 }
854 }
855 };
856
857 /**
858 * Notifies this component that it now has a parent component. When this
859 * method is invoked, the chain of parent components is set up with
860 * KeyboardAction event listeners.
861 */
862 @Override
863 public void addNotify() {
864 super.addNotify();
865
866 registerFinishListener();
867
868 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
869 JFXPanel.this.getToolkit().addAWTEventListener(ungrabListener,
870 SwingInterOpUtils.GRAB_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
871 return null;
872 });
873 updateComponentSize(); // see RT-23603
874 SwingFXUtils.runOnFxThread(() -> {
875 if ((stage != null) && !stage.isShowing()) {
876 stage.show();
877 sendMoveEventToFX();
878 }
879 });
880 }
881
882 @Override
883 public InputMethodRequests getInputMethodRequests() {
884 EmbeddedSceneInterface scene = scenePeer;
885 if (scene == null) {
886 return null;
887 }
888 return new InputMethodSupport.InputMethodRequestsAdapter(scene.getInputMethodRequests());
889 }
890
901 });
902
903 pixelsIm = null;
904 pWidth = 0;
905 pHeight = 0;
906
907 super.removeNotify();
908
909 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
910 JFXPanel.this.getToolkit().removeAWTEventListener(ungrabListener);
911 return null;
912 });
913
914 /* see CR 4867453 */
915 getInputContext().removeNotify(this);
916
917 deregisterFinishListener();
918 }
919
920 private void invokeOnClientEDT(Runnable r) {
921 SwingInterOpUtils.postEvent(this, new InvocationEvent(this, r));
922 }
923
924 private class HostContainer implements HostInterface {
925
926 @Override
927 public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) {
928 stagePeer = embeddedStage;
929 if (stagePeer == null) {
930 return;
931 }
932 if (pWidth > 0 && pHeight > 0) {
933 stagePeer.setSize(pWidth, pHeight);
934 }
935 invokeOnClientEDT(() -> {
936 if (stagePeer != null && JFXPanel.this.isFocusOwner()) {
937 stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED);
938 }
939 });
940 sendMoveEventToFX();
941 }
1022 return cachedPlatformCursor;
1023 }
1024
1025 // platform cursor not cached yet
1026 final Cursor platformCursor =
1027 SwingCursors.embedCursorToCursor(cursorFrame);
1028 cursorFrame.setPlatforCursor(Cursor.class, platformCursor);
1029
1030 return platformCursor;
1031 }
1032
1033 @Override
1034 public boolean grabFocus() {
1035 // On X11 grab is limited to a single XDisplay connection,
1036 // so we can't delegate it to another GUI toolkit.
1037 if (PlatformUtil.isLinux()) return true;
1038
1039 invokeOnClientEDT(() -> {
1040 Window window = SwingUtilities.getWindowAncestor(JFXPanel.this);
1041 if (window != null) {
1042 SwingInterOpUtils.grab(JFXPanel.this.getToolkit(), window);
1043 }
1044 });
1045
1046 return true; // Oh, well...
1047 }
1048
1049 @Override
1050 public void ungrabFocus() {
1051 // On X11 grab is limited to a single XDisplay connection,
1052 // so we can't delegate it to another GUI toolkit.
1053 if (PlatformUtil.isLinux()) return;
1054
1055 invokeOnClientEDT(() -> {
1056 Window window = SwingUtilities.getWindowAncestor(JFXPanel.this);
1057 if (window != null) {
1058 SwingInterOpUtils.ungrab(JFXPanel.this.getToolkit(), window);
1059 }
1060 });
1061 }
1062 }
1063 }
|