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 }
|