< prev index next >

modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java

Print this page




  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.embed.swing;
  27 
  28 import java.awt.AlphaComposite;
  29 import java.awt.AWTEvent;
  30 import java.awt.Component;
  31 import java.awt.Cursor;
  32 import java.awt.Dimension;
  33 import java.awt.Graphics;
  34 import java.awt.Graphics2D;
  35 import java.awt.KeyboardFocusManager;
  36 import java.awt.Point;
  37 import java.awt.Window;
  38 import java.awt.Insets;
  39 import java.awt.EventQueue;
  40 import java.awt.SecondaryLoop;

  41 import java.awt.event.AWTEventListener;
  42 import java.awt.event.ComponentEvent;
  43 import java.awt.event.FocusEvent;
  44 import java.awt.event.HierarchyEvent;
  45 import java.awt.event.InputEvent;
  46 import java.awt.event.InputMethodEvent;
  47 import java.awt.event.KeyEvent;
  48 import java.awt.event.MouseEvent;
  49 import java.awt.event.MouseWheelEvent;
  50 import java.awt.event.FocusAdapter;
  51 import java.awt.event.FocusListener;
  52 import java.awt.im.InputMethodRequests;
  53 import java.awt.image.BufferedImage;
  54 import java.awt.image.DataBufferInt;
  55 import java.awt.datatransfer.Clipboard;
  56 import java.nio.IntBuffer;
  57 import java.security.AccessController;
  58 import java.security.PrivilegedAction;
  59 import java.util.concurrent.CountDownLatch;
  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


 168     private volatile int pPreferredHeight = -1;
 169 
 170     // Cached copy of this component's location on screen to avoid
 171     // calling getLocationOnScreen() under the tree lock on FX thread
 172     private volatile int screenX = 0;
 173     private volatile int screenY = 0;
 174 
 175     // Accessed on EDT only
 176     private BufferedImage pixelsIm;
 177 
 178     private volatile float opacity = 1.0f;
 179 
 180     // Indicates how many times setFxEnabled(false) has been called.
 181     // A value of 0 means the component is enabled.
 182     private AtomicInteger disableCount = new AtomicInteger(0);
 183 
 184     private boolean isCapturingMouse = false;
 185 
 186     private static boolean fxInitialized;
 187 











 188     private synchronized void registerFinishListener() {
 189         if (instanceCount.getAndIncrement() > 0) {
 190             // Already registered
 191             return;
 192         }
 193         // Need to install a finish listener to catch calls to Platform.exit
 194         finishListener = new PlatformImpl.FinishListener() {
 195             @Override public void idle(boolean implicitExit) {
 196             }
 197             @Override public void exitCalled() {
 198             }
 199         };
 200         PlatformImpl.addListener(finishListener);
 201     }
 202 
 203     private synchronized void deregisterFinishListener() {
 204         if (instanceCount.decrementAndGet() > 0) {
 205             // Other JFXPanels still alive
 206             return;
 207         }


 239                     throw (Error) th[0];
 240                 }
 241                 throw new RuntimeException("FX initialization failed", th[0]);
 242             }
 243         } else {
 244             PlatformImpl.startup(() -> {});
 245         }
 246         fxInitialized = true;
 247     }
 248 
 249     /**
 250      * Creates a new {@code JFXPanel} object.
 251      * <p>
 252      * <b>Implementation note</b>: when the first {@code JFXPanel} object
 253      * is created, it implicitly initializes the JavaFX runtime. This is the
 254      * preferred way to initialize JavaFX in Swing.
 255      */
 256     public JFXPanel() {
 257         super();
 258 

 259         initFx();
 260 
 261         hostContainer = new HostContainer();
 262 
 263         enableEvents(InputEvent.COMPONENT_EVENT_MASK |
 264                      InputEvent.FOCUS_EVENT_MASK |
 265                      InputEvent.HIERARCHY_BOUNDS_EVENT_MASK |
 266                      InputEvent.HIERARCHY_EVENT_MASK |
 267                      InputEvent.MOUSE_EVENT_MASK |
 268                      InputEvent.MOUSE_MOTION_EVENT_MASK |
 269                      InputEvent.MOUSE_WHEEL_EVENT_MASK |
 270                      InputEvent.KEY_EVENT_MASK |
 271                      InputEvent.INPUT_METHOD_EVENT_MASK);
 272 
 273         setFocusable(true);
 274         setFocusTraversalKeysEnabled(false);
 275     }
 276 
 277     /**
 278      * Returns the JavaFX scene attached to this {@code JFXPanel}.


 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) {
 852                             // No need to check if grab is active or not.
 853                             // NoAutoHide popups don't request the grab and
 854                             // ignore the Ungrab event anyway.
 855                             // AutoHide popups actually should be hidden when
 856                             // user clicks some non-FX content, even if for
 857                             // some reason they didn't install the grab when
 858                             // they were shown.
 859                             JFXPanel.this.stagePeer.focusUngrab();
 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 
 901     /**
 902      * Notifies this component that it no longer has a parent component.
 903      * When this method is invoked, any KeyboardActions set up in the the
 904      * chain of parent components are removed.
 905      */
 906     @Override public void removeNotify() {
 907         SwingFXUtils.runOnFxThread(() -> {
 908             if ((stage != null) && stage.isShowing()) {
 909                 stage.hide();
 910             }
 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 }


  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.embed.swing;
  27 
  28 import java.awt.AlphaComposite;
  29 import java.awt.AWTEvent;
  30 import java.awt.Component;
  31 import java.awt.Cursor;
  32 import java.awt.Dimension;
  33 import java.awt.Graphics;
  34 import java.awt.Graphics2D;
  35 import java.awt.KeyboardFocusManager;
  36 import java.awt.Point;
  37 import java.awt.Window;
  38 import java.awt.Insets;
  39 import java.awt.EventQueue;
  40 import java.awt.SecondaryLoop;
  41 import java.awt.GraphicsEnvironment;
  42 import java.awt.event.AWTEventListener;
  43 import java.awt.event.ComponentEvent;
  44 import java.awt.event.FocusEvent;
  45 import java.awt.event.HierarchyEvent;
  46 import java.awt.event.InputEvent;
  47 import java.awt.event.InputMethodEvent;
  48 import java.awt.event.KeyEvent;
  49 import java.awt.event.MouseEvent;
  50 import java.awt.event.MouseWheelEvent;
  51 import java.awt.event.FocusAdapter;
  52 import java.awt.event.FocusListener;
  53 import java.awt.im.InputMethodRequests;
  54 import java.awt.image.BufferedImage;
  55 import java.awt.image.DataBufferInt;
  56 import java.awt.datatransfer.Clipboard;
  57 import java.nio.IntBuffer;
  58 import java.security.AccessController;
  59 import java.security.PrivilegedAction;
  60 import java.util.concurrent.CountDownLatch;
  61 
  62 import javafx.application.Platform;
  63 import javafx.scene.Scene;
  64 
  65 import javax.swing.JComponent;
  66 import javax.swing.SwingUtilities;
  67 
  68 import com.sun.javafx.application.PlatformImpl;
  69 import com.sun.javafx.cursor.CursorFrame;
  70 import com.sun.javafx.embed.AbstractEvents;
  71 import com.sun.javafx.embed.EmbeddedSceneInterface;
  72 import com.sun.javafx.embed.EmbeddedStageInterface;
  73 import com.sun.javafx.embed.HostInterface;
  74 import com.sun.javafx.stage.EmbeddedWindow;
  75 import com.sun.javafx.tk.Toolkit;
  76 import com.sun.javafx.PlatformUtil;
  77 import java.awt.event.InvocationEvent;
  78 
  79 import java.lang.reflect.Method;
  80 import java.util.concurrent.atomic.AtomicInteger;
  81 



  82 import com.sun.javafx.logging.PlatformLogger;
  83 import com.sun.javafx.logging.PlatformLogger.Level;
  84 
  85 import com.sun.javafx.embed.swing.InteropFactory;
  86 import com.sun.javafx.embed.swing.SwingDnD;
  87 import com.sun.javafx.embed.swing.SwingEvents;
  88 import com.sun.javafx.embed.swing.SwingCursors;
  89 import com.sun.javafx.embed.swing.SwingNodeHelper;
  90 import com.sun.javafx.embed.swing.JFXPanelInterop;
  91 
  92 /**
  93 * {@code JFXPanel} is a component to embed JavaFX content into
  94  * Swing applications. The content to be displayed is specified
  95  * with the {@link #setScene} method that accepts an instance of
  96  * JavaFX {@code Scene}. After the scene is assigned, it gets
  97  * repainted automatically. All the input and focus events are
  98  * forwarded to the scene transparently to the developer.
  99  * <p>
 100  * There are some restrictions related to {@code JFXPanel}. As a
 101  * Swing component, it should only be accessed from the event
 102  * dispatch thread, except the {@link #setScene} method, which can
 103  * be called either on the event dispatch thread or on the JavaFX
 104  * application thread.
 105  * <p>
 106  * Here is a typical pattern how {@code JFXPanel} can used:
 107  * <pre>
 108  *     public class Test {
 109  *
 110  *         private static void initAndShowGUI() {
 111  *             // This method is invoked on Swing thread


 173     private volatile int pPreferredHeight = -1;
 174 
 175     // Cached copy of this component's location on screen to avoid
 176     // calling getLocationOnScreen() under the tree lock on FX thread
 177     private volatile int screenX = 0;
 178     private volatile int screenY = 0;
 179 
 180     // Accessed on EDT only
 181     private BufferedImage pixelsIm;
 182 
 183     private volatile float opacity = 1.0f;
 184 
 185     // Indicates how many times setFxEnabled(false) has been called.
 186     // A value of 0 means the component is enabled.
 187     private AtomicInteger disableCount = new AtomicInteger(0);
 188 
 189     private boolean isCapturingMouse = false;
 190 
 191     private static boolean fxInitialized;
 192 
 193     private static InteropFactory instance = null;
 194     private JFXPanelInterop jfxPaneliop;
 195 
 196     static {
 197         try {
 198             instance = InteropFactory.getInstance();
 199         } catch (Exception e) {
 200             throw new ExceptionInInitializerError(e);
 201         }
 202     }
 203 
 204     private synchronized void registerFinishListener() {
 205         if (instanceCount.getAndIncrement() > 0) {
 206             // Already registered
 207             return;
 208         }
 209         // Need to install a finish listener to catch calls to Platform.exit
 210         finishListener = new PlatformImpl.FinishListener() {
 211             @Override public void idle(boolean implicitExit) {
 212             }
 213             @Override public void exitCalled() {
 214             }
 215         };
 216         PlatformImpl.addListener(finishListener);
 217     }
 218 
 219     private synchronized void deregisterFinishListener() {
 220         if (instanceCount.decrementAndGet() > 0) {
 221             // Other JFXPanels still alive
 222             return;
 223         }


 255                     throw (Error) th[0];
 256                 }
 257                 throw new RuntimeException("FX initialization failed", th[0]);
 258             }
 259         } else {
 260             PlatformImpl.startup(() -> {});
 261         }
 262         fxInitialized = true;
 263     }
 264 
 265     /**
 266      * Creates a new {@code JFXPanel} object.
 267      * <p>
 268      * <b>Implementation note</b>: when the first {@code JFXPanel} object
 269      * is created, it implicitly initializes the JavaFX runtime. This is the
 270      * preferred way to initialize JavaFX in Swing.
 271      */
 272     public JFXPanel() {
 273         super();
 274 
 275         jfxPaneliop = instance.createJFXPanelImpl();
 276         initFx();
 277 
 278         hostContainer = new HostContainer();
 279 
 280         enableEvents(InputEvent.COMPONENT_EVENT_MASK |
 281                      InputEvent.FOCUS_EVENT_MASK |
 282                      InputEvent.HIERARCHY_BOUNDS_EVENT_MASK |
 283                      InputEvent.HIERARCHY_EVENT_MASK |
 284                      InputEvent.MOUSE_EVENT_MASK |
 285                      InputEvent.MOUSE_MOTION_EVENT_MASK |
 286                      InputEvent.MOUSE_WHEEL_EVENT_MASK |
 287                      InputEvent.KEY_EVENT_MASK |
 288                      InputEvent.INPUT_METHOD_EVENT_MASK);
 289 
 290         setFocusable(true);
 291         setFocusTraversalKeysEnabled(false);
 292     }
 293 
 294     /**
 295      * Returns the JavaFX scene attached to this {@code JFXPanel}.


 450 
 451     /**
 452      * Overrides the {@link java.awt.Component#processMouseEvent(MouseEvent)}
 453      * method to dispatch the mouse event to the JavaFX scene attached to this
 454      * {@code JFXPanel}.
 455      *
 456      * @param e the mouse event to dispatch to the JavaFX scene
 457      */
 458     @Override
 459     protected void processMouseEvent(MouseEvent e) {
 460         if ((e.getID() == MouseEvent.MOUSE_PRESSED) &&
 461             (e.getButton() == MouseEvent.BUTTON1)) {
 462             if (!hasFocus()) {
 463                 requestFocus();
 464                 // this focus request event goes to eventqueue and will be
 465                 // asynchronously handled so MOUSE_PRESSED event will not be
 466                 // honoured by FX immediately due to lack of focus in fx
 467                 // component. Fire the same MOUSE_PRESSED event after
 468                 // requestFocus() so that 2nd mouse press will be honoured
 469                 // since now fx have focus
 470                 jfxPaneliop.postEvent(this, e);



 471             }
 472         }
 473 
 474         sendMouseEventToFX(e);
 475         super.processMouseEvent(e);
 476     }
 477 
 478     /**
 479      * Overrides the {@link java.awt.Component#processMouseMotionEvent(MouseEvent)}
 480      * method to dispatch the mouse motion event to the JavaFX scene attached to
 481      * this {@code JFXPanel}.
 482      *
 483      * @param e the mouse motion event to dispatch to the JavaFX scene
 484      */
 485     @Override
 486     protected void processMouseMotionEvent(MouseEvent e) {
 487         sendMouseEventToFX(e);
 488         super.processMouseMotionEvent(e);
 489     }
 490 


 567         }
 568         super.processComponentEvent(e);
 569     }
 570 
 571     // called on EDT only
 572     private void updateComponentSize() {
 573         int oldWidth = pWidth;
 574         int oldHeight = pHeight;
 575         // It's quite possible to get negative values here, this is not
 576         // what JavaFX embedded scenes/stages are ready to
 577         pWidth = Math.max(0, getWidth());
 578         pHeight = Math.max(0, getHeight());
 579         if (getBorder() != null) {
 580             Insets i = getBorder().getBorderInsets(this);
 581             pWidth -= (i.left + i.right);
 582             pHeight -= (i.top + i.bottom);
 583         }
 584         double newScaleFactorX = scaleFactorX;
 585         double newScaleFactorY = scaleFactorY;
 586         Graphics g = getGraphics();
 587         newScaleFactorX = GraphicsEnvironment.getLocalGraphicsEnvironment().
 588                           getDefaultScreenDevice().getDefaultConfiguration().
 589                           getDefaultTransform().getScaleX();
 590         newScaleFactorY = GraphicsEnvironment.getLocalGraphicsEnvironment().
 591                           getDefaultScreenDevice().getDefaultConfiguration().
 592                           getDefaultTransform().getScaleY();
 593         if (oldWidth != pWidth || oldHeight != pHeight ||
 594             newScaleFactorX != scaleFactorX || newScaleFactorY != scaleFactorY)
 595         {
 596             createResizePixelBuffer(newScaleFactorX, newScaleFactorY);
 597             if (scenePeer != null) {
 598                 scenePeer.setPixelScaleFactors((float) newScaleFactorX,
 599                                                (float) newScaleFactorY);
 600             }
 601             scaleFactorX = newScaleFactorX;
 602             scaleFactorY = newScaleFactorY;
 603             sendResizeEventToFX();
 604         }
 605     }
 606 
 607     // This methods should only be called on EDT
 608     private boolean updateScreenLocation() {
 609         synchronized (getTreeLock()) {
 610             if (isShowing()) {
 611                 Point p = getLocationOnScreen();
 612                 screenX = p.x;


 763         if (!scenePeer.getPixels(buf, pWidth, pHeight)) {
 764             // In this case we just render what we have so far in the buffer.
 765         }
 766 
 767         Graphics gg = null;
 768         try {
 769             gg = g.create();
 770             if ((opacity < 1.0f) && (gg instanceof Graphics2D)) {
 771                 Graphics2D g2d = (Graphics2D)gg;
 772                 AlphaComposite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
 773                 g2d.setComposite(c);
 774             }
 775             if (getBorder() != null) {
 776                 Insets i = getBorder().getBorderInsets(this);
 777                 gg.translate(i.left, i.top);
 778             }
 779             gg.drawImage(pixelsIm, 0, 0, pWidth, pHeight, null);
 780 
 781             double newScaleFactorX = scaleFactorX;
 782             double newScaleFactorY = scaleFactorY;
 783             newScaleFactorX = GraphicsEnvironment.getLocalGraphicsEnvironment().
 784                               getDefaultScreenDevice().getDefaultConfiguration().
 785                               getDefaultTransform().getScaleX();
 786             newScaleFactorY = GraphicsEnvironment.getLocalGraphicsEnvironment().
 787                               getDefaultScreenDevice().getDefaultConfiguration().
 788                               getDefaultTransform().getScaleY();
 789             if (scaleFactorX != newScaleFactorX || scaleFactorY != newScaleFactorY) {
 790                 createResizePixelBuffer(newScaleFactorX, newScaleFactorY);
 791                 // The scene will request repaint.
 792                 scenePeer.setPixelScaleFactors((float) newScaleFactorX,
 793                                                (float) newScaleFactorY);
 794                 scaleFactorX = newScaleFactorX;
 795                 scaleFactorY = newScaleFactorY;
 796             }
 797         } catch (Throwable th) {
 798             th.printStackTrace();
 799         } finally {
 800             if (gg != null) {
 801                 gg.dispose();
 802             }
 803         }
 804     }
 805 
 806     /**
 807      * Returns the preferred size of this {@code JFXPanel}, either
 808      * previously set with {@link #setPreferredSize(Dimension)} or


 827         if (!enabled) {
 828             if (disableCount.incrementAndGet() == 1) {
 829                 if (dnd != null) {
 830                     dnd.removeNotify();
 831                 }
 832             }
 833         } else {
 834             if (disableCount.get() == 0) {
 835                 //should report a warning about an extra enable call ?
 836                 return;
 837             }
 838             if (disableCount.decrementAndGet() == 0) {
 839                 if (dnd != null) {
 840                     dnd.addNotify();
 841                 }
 842             }
 843         }
 844     }
 845 
 846     private transient  AWTEventListener ungrabListener = event -> {
 847         if (jfxPaneliop.isUngrabEvent(event)) {
 848             SwingNodeHelper.runOnFxThread(() -> {
 849                 if (JFXPanel.this.stagePeer != null &&
 850                         getScene() != null &&
 851                         getScene().getFocusOwner() != null &&
 852                         getScene().getFocusOwner().isFocused()) {
 853                     JFXPanel.this.stagePeer.focusUngrab();
 854                 }
 855             });
 856         }
 857         if (event instanceof MouseEvent) {
 858             // Synthesize FOCUS_UNGRAB if user clicks the AWT top-level window
 859             // that contains the JFXPanel.
 860             if (event.getID() == MouseEvent.MOUSE_PRESSED && event.getSource() instanceof Component) {
 861                 final Window jfxPanelWindow = SwingUtilities.getWindowAncestor(JFXPanel.this);
 862                 final Component source = (Component)event.getSource();
 863                 final Window eventWindow = source instanceof Window ? (Window)source : SwingUtilities.getWindowAncestor(source);
 864 
 865                 if (jfxPanelWindow == eventWindow) {
 866                     SwingNodeHelper.runOnFxThread(() -> {
 867                         if (JFXPanel.this.stagePeer != null) {
 868                             // No need to check if grab is active or not.
 869                             // NoAutoHide popups don't request the grab and
 870                             // ignore the Ungrab event anyway.
 871                             // AutoHide popups actually should be hidden when
 872                             // user clicks some non-FX content, even if for
 873                             // some reason they didn't install the grab when
 874                             // they were shown.
 875                             JFXPanel.this.stagePeer.focusUngrab();
 876                         }
 877                     });
 878                 }
 879             }
 880         }
 881     };
 882 
 883     /**
 884      * Notifies this component that it now has a parent component. When this
 885      * method is invoked, the chain of parent components is set up with
 886      * KeyboardAction event listeners.
 887      */
 888     @Override
 889     public void addNotify() {
 890         super.addNotify();
 891 
 892         registerFinishListener();
 893 
 894         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 895             JFXPanel.this.getToolkit().addAWTEventListener(ungrabListener,
 896             jfxPaneliop.setMask());
 897             return null;
 898         });
 899         updateComponentSize(); // see RT-23603
 900         SwingNodeHelper.runOnFxThread(() -> {
 901             if ((stage != null) && !stage.isShowing()) {
 902                 stage.show();
 903                 sendMoveEventToFX();
 904             }
 905         });
 906     }
 907 
 908     @Override
 909     public InputMethodRequests getInputMethodRequests() {
 910         EmbeddedSceneInterface scene = scenePeer;
 911         if (scene == null) {
 912             return null;
 913         }
 914         return new InputMethodSupport.InputMethodRequestsAdapter(scene.getInputMethodRequests());
 915     }
 916 
 917     /**
 918      * Notifies this component that it no longer has a parent component.
 919      * When this method is invoked, any KeyboardActions set up in the the
 920      * chain of parent components are removed.
 921      */
 922     @Override public void removeNotify() {
 923         SwingNodeHelper.runOnFxThread(() -> {
 924             if ((stage != null) && stage.isShowing()) {
 925                 stage.hide();
 926             }
 927         });
 928 
 929         pixelsIm = null;
 930         pWidth = 0;
 931         pHeight = 0;
 932 
 933         super.removeNotify();
 934 
 935         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 936             JFXPanel.this.getToolkit().removeAWTEventListener(ungrabListener);
 937             return null;
 938         });
 939 
 940         /* see CR 4867453 */
 941         getInputContext().removeNotify(this);
 942 
 943         deregisterFinishListener();
 944     }
 945 
 946     private void invokeOnClientEDT(Runnable r) {
 947         jfxPaneliop.postEvent(this, new InvocationEvent(this, r));





 948     }
 949 
 950     private class HostContainer implements HostInterface {
 951 
 952         @Override
 953         public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) {
 954             stagePeer = embeddedStage;
 955             if (stagePeer == null) {
 956                 return;
 957             }
 958             if (pWidth > 0 && pHeight > 0) {
 959                 stagePeer.setSize(pWidth, pHeight);
 960             }
 961             invokeOnClientEDT(() -> {
 962                 if (stagePeer != null && JFXPanel.this.isFocusOwner()) {
 963                     stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED);
 964                 }
 965             });
 966             sendMoveEventToFX();
 967         }


1048                 return cachedPlatformCursor;
1049             }
1050 
1051             // platform cursor not cached yet
1052             final Cursor platformCursor =
1053                     SwingCursors.embedCursorToCursor(cursorFrame);
1054             cursorFrame.setPlatforCursor(Cursor.class, platformCursor);
1055 
1056             return platformCursor;
1057         }
1058 
1059         @Override
1060         public boolean grabFocus() {
1061             // On X11 grab is limited to a single XDisplay connection,
1062             // so we can't delegate it to another GUI toolkit.
1063             if (PlatformUtil.isLinux()) return true;
1064 
1065             invokeOnClientEDT(() -> {
1066                 Window window = SwingUtilities.getWindowAncestor(JFXPanel.this);
1067                 if (window != null) {
1068                     jfxPaneliop.grab(JFXPanel.this.getToolkit(), window);


1069                 }
1070             });
1071 
1072             return true; // Oh, well...
1073         }
1074 
1075         @Override
1076         public void ungrabFocus() {
1077             // On X11 grab is limited to a single XDisplay connection,
1078             // so we can't delegate it to another GUI toolkit.
1079             if (PlatformUtil.isLinux()) return;
1080 
1081             invokeOnClientEDT(() -> {
1082                 Window window = SwingUtilities.getWindowAncestor(JFXPanel.this);
1083                 if (window != null) {
1084                     jfxPaneliop.ungrab(JFXPanel.this.getToolkit(), window);


1085                 }
1086             });
1087         }
1088     }
1089 }
< prev index next >