/*
 * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.embed.swing;

import java.awt.AlphaComposite;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Window;
import java.awt.Insets;
import java.awt.EventQueue;
import java.awt.SecondaryLoop;
import java.awt.GraphicsEnvironment;
import java.awt.event.AWTEventListener;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.InputEvent;
import java.awt.event.InputMethodEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.InvocationEvent;
import java.awt.im.InputMethodRequests;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.nio.IntBuffer;
import java.util.concurrent.atomic.AtomicInteger;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;

import javafx.application.Platform;
import javafx.scene.Scene;

import com.sun.javafx.application.PlatformImpl;
import com.sun.javafx.cursor.CursorFrame;
import com.sun.javafx.stage.EmbeddedWindow;
import com.sun.javafx.tk.Toolkit;
import com.sun.javafx.PlatformUtil;

import com.sun.javafx.logging.PlatformLogger;
import com.sun.javafx.embed.AbstractEvents;
import com.sun.javafx.embed.EmbeddedSceneInterface;
import com.sun.javafx.embed.EmbeddedStageInterface;
import com.sun.javafx.embed.HostInterface;

import com.sun.javafx.embed.swing.SwingDnD;
import com.sun.javafx.embed.swing.SwingEvents;
import com.sun.javafx.embed.swing.SwingCursors;
import com.sun.javafx.embed.swing.SwingNodeHelper;
import com.sun.javafx.embed.swing.newimpl.JFXPanelInteropN;

JFXPanel is a component to embed JavaFX content into Swing applications. The content to be displayed is specified with the setScene method that accepts an instance of JavaFX Scene. After the scene is assigned, it gets repainted automatically. All the input and focus events are forwarded to the scene transparently to the developer.

There are some restrictions related to JFXPanel. As a Swing component, it should only be accessed from the event dispatch thread, except the setScene method, which can be called either on the event dispatch thread or on the JavaFX application thread.

Here is a typical pattern how JFXPanel can used:

    public class Test {
        private static void initAndShowGUI() {
            // This method is invoked on Swing thread
            JFrame frame = new JFrame("FX");
            final JFXPanel fxPanel = new JFXPanel();
            frame.add(fxPanel);
            frame.setVisible(true);
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    initFX(fxPanel);
                }
            });
        }
        private static void initFX(JFXPanel fxPanel) {
            // This method is invoked on JavaFX thread
            Scene scene = createScene();
            fxPanel.setScene(scene);
        }
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    initAndShowGUI();
                }
            });
        }
    }
Since:JavaFX 2.0
/** * {@code JFXPanel} is a component to embed JavaFX content into * Swing applications. The content to be displayed is specified * with the {@link #setScene} method that accepts an instance of * JavaFX {@code Scene}. After the scene is assigned, it gets * repainted automatically. All the input and focus events are * forwarded to the scene transparently to the developer. * <p> * There are some restrictions related to {@code JFXPanel}. As a * Swing component, it should only be accessed from the event * dispatch thread, except the {@link #setScene} method, which can * be called either on the event dispatch thread or on the JavaFX * application thread. * <p> * Here is a typical pattern how {@code JFXPanel} can used: * <pre> * public class Test { * * private static void initAndShowGUI() { * // This method is invoked on Swing thread * JFrame frame = new JFrame("FX"); * final JFXPanel fxPanel = new JFXPanel(); * frame.add(fxPanel); * frame.setVisible(true); * * Platform.runLater(new Runnable() { * &#064;Override * public void run() { * initFX(fxPanel); * } * }); * } * * private static void initFX(JFXPanel fxPanel) { * // This method is invoked on JavaFX thread * Scene scene = createScene(); * fxPanel.setScene(scene); * } * * public static void main(String[] args) { * SwingUtilities.invokeLater(new Runnable() { * &#064;Override * public void run() { * initAndShowGUI(); * } * }); * } * } * </pre> * * @since JavaFX 2.0 */
public class JFXPanel extends JComponent { private final static PlatformLogger log = PlatformLogger.getLogger(JFXPanel.class.getName()); private static AtomicInteger instanceCount = new AtomicInteger(0); private static PlatformImpl.FinishListener finishListener; private transient HostContainer hostContainer; private transient volatile EmbeddedWindow stage; private transient volatile Scene scene; // Accessed on EDT only private transient SwingDnD dnd; private transient EmbeddedStageInterface stagePeer; private transient EmbeddedSceneInterface scenePeer; // The logical size of the FX content private int pWidth; private int pHeight; // The scale factor, used to translate b/w the logical (the FX content dimension) // and physical (the back buffer's dimension) coordinate spaces private double scaleFactorX = 1.0; private double scaleFactorY = 1.0; // Preferred size set from FX private volatile int pPreferredWidth = -1; private volatile int pPreferredHeight = -1; // Cached copy of this component's location on screen to avoid // calling getLocationOnScreen() under the tree lock on FX thread private volatile int screenX = 0; private volatile int screenY = 0; // Accessed on EDT only private BufferedImage pixelsIm; private volatile float opacity = 1.0f; // Indicates how many times setFxEnabled(false) has been called. // A value of 0 means the component is enabled. private AtomicInteger disableCount = new AtomicInteger(0); private boolean isCapturingMouse = false; private static boolean fxInitialized; private JFXPanelInteropN jfxPanelIOP; private synchronized void registerFinishListener() { if (instanceCount.getAndIncrement() > 0) { // Already registered return; } // Need to install a finish listener to catch calls to Platform.exit finishListener = new PlatformImpl.FinishListener() { @Override public void idle(boolean implicitExit) { } @Override public void exitCalled() { } }; PlatformImpl.addListener(finishListener); } private synchronized void deregisterFinishListener() { if (instanceCount.decrementAndGet() > 0) { // Other JFXPanels still alive return; } PlatformImpl.removeListener(finishListener); finishListener = null; } // Initialize FX runtime when the JFXPanel instance is constructed private synchronized static void initFx() { // Note that calling PlatformImpl.startup more than once is OK if (fxInitialized) { return; } EventQueue eventQueue = AccessController.doPrivileged( (PrivilegedAction<EventQueue>) java.awt.Toolkit .getDefaultToolkit()::getSystemEventQueue); if (eventQueue.isDispatchThread()) { // We won't block EDT by FX initialization SecondaryLoop secondaryLoop = eventQueue.createSecondaryLoop(); final Throwable[] th = {null}; new Thread(() -> { try { PlatformImpl.startup(() -> {}); } catch (Throwable t) { th[0] = t; } finally { secondaryLoop.exit(); } }).start(); secondaryLoop.enter(); if (th[0] != null) { if (th[0] instanceof RuntimeException) { throw (RuntimeException) th[0]; } else if (th[0] instanceof Error) { throw (Error) th[0]; } throw new RuntimeException("FX initialization failed", th[0]); } } else { PlatformImpl.startup(() -> {}); } fxInitialized = true; }
Creates a new JFXPanel object.

Implementation note: when the first JFXPanel object is created, it implicitly initializes the JavaFX runtime. This is the preferred way to initialize JavaFX in Swing.

/** * Creates a new {@code JFXPanel} object. * <p> * <b>Implementation note</b>: when the first {@code JFXPanel} object * is created, it implicitly initializes the JavaFX runtime. This is the * preferred way to initialize JavaFX in Swing. */
public JFXPanel() { super(); jfxPanelIOP = new JFXPanelInteropN(); initFx(); hostContainer = new HostContainer(); enableEvents(InputEvent.COMPONENT_EVENT_MASK | InputEvent.FOCUS_EVENT_MASK | InputEvent.HIERARCHY_BOUNDS_EVENT_MASK | InputEvent.HIERARCHY_EVENT_MASK | InputEvent.MOUSE_EVENT_MASK | InputEvent.MOUSE_MOTION_EVENT_MASK | InputEvent.MOUSE_WHEEL_EVENT_MASK | InputEvent.KEY_EVENT_MASK | InputEvent.INPUT_METHOD_EVENT_MASK); setFocusable(true); setFocusTraversalKeysEnabled(false); }
Returns the JavaFX scene attached to this JFXPanel.
Returns:the Scene attached to this JFXPanel
/** * Returns the JavaFX scene attached to this {@code JFXPanel}. * * @return the {@code Scene} attached to this {@code JFXPanel} */
public Scene getScene() { return scene; }
Attaches a Scene object to display in this JFXPanel. This method can be called either on the event dispatch thread or the JavaFX application thread.
Params:
  • newScene – a scene to display in this JFXpanel
See Also:
/** * Attaches a {@code Scene} object to display in this {@code * JFXPanel}. This method can be called either on the event * dispatch thread or the JavaFX application thread. * * @param newScene a scene to display in this {@code JFXpanel} * * @see java.awt.EventQueue#isDispatchThread() * @see javafx.application.Platform#isFxApplicationThread() */
public void setScene(final Scene newScene) { if (Toolkit.getToolkit().isFxUserThread()) { setSceneImpl(newScene); } else { EventQueue eventQueue = AccessController.doPrivileged( (PrivilegedAction<EventQueue>) java.awt.Toolkit .getDefaultToolkit()::getSystemEventQueue); SecondaryLoop secondaryLoop = eventQueue.createSecondaryLoop(); if (secondaryLoop.enter()) { Platform.runLater(() -> { setSceneImpl(newScene); }); secondaryLoop.exit(); } } } /* * Called on JavaFX app thread. */ private void setSceneImpl(Scene newScene) { if ((stage != null) && (newScene == null)) { stage.hide(); stage = null; } scene = newScene; if ((stage == null) && (newScene != null)) { stage = new EmbeddedWindow(hostContainer); } if (stage != null) { stage.setScene(newScene); if (!stage.isShowing()) { stage.show(); } } }
JFXPanel's opacity is controlled by the JavaFX content which is displayed in this component, so this method overrides JComponent.setOpaque(boolean) to only accept a false value. If this method is called with a true value, no action is performed.
Params:
  • opaque – must be false
/** * {@code JFXPanel}'s opacity is controlled by the JavaFX content * which is displayed in this component, so this method overrides * {@link javax.swing.JComponent#setOpaque(boolean)} to only accept a * {@code false} value. If this method is called with a {@code true} * value, no action is performed. * * @param opaque must be {@code false} */
@Override public final void setOpaque(boolean opaque) { // Don't let user control opacity if (!opaque) { super.setOpaque(opaque); } }
JFXPanel's opacity is controlled by the JavaFX content which is displayed in this component, so this method overrides JComponent.isOpaque() to always return a false value.
Returns:a false value
/** * {@code JFXPanel}'s opacity is controlled by the JavaFX content * which is displayed in this component, so this method overrides * {@link javax.swing.JComponent#isOpaque()} to always return a * {@code false} value. * * @return a {@code false} value */
@Override public final boolean isOpaque() { return false; } private void sendMouseEventToFX(MouseEvent e) { if (scenePeer == null || !isFxEnabled()) { return; } // FX only supports 5 buttons so don't send the event for other buttons switch (e.getID()) { case MouseEvent.MOUSE_DRAGGED: case MouseEvent.MOUSE_PRESSED: case MouseEvent.MOUSE_RELEASED: if (e.getButton() > 5) return; break; } int extModifiers = e.getModifiersEx(); // Fix for RT-15457: we should report no mouse button upon mouse release, so // *BtnDown values are calculated based on extMofifiers, not e.getButton() boolean primaryBtnDown = (extModifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0; boolean middleBtnDown = (extModifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0; boolean secondaryBtnDown = (extModifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0; boolean backBtnDown = (extModifiers & MouseEvent.getMaskForButton(4)) != 0; boolean forwardBtnDown = (extModifiers & MouseEvent.getMaskForButton(5)) != 0; // Fix for RT-16558: if a PRESSED event is consumed, e.g. by a Swing Popup, // subsequent DRAGGED and RELEASED events should not be sent to FX as well if (e.getID() == MouseEvent.MOUSE_DRAGGED) { if (!isCapturingMouse) { return; } } else if (e.getID() == MouseEvent.MOUSE_PRESSED) { isCapturingMouse = true; } else if (e.getID() == MouseEvent.MOUSE_RELEASED) { if (!isCapturingMouse) { return; } isCapturingMouse = primaryBtnDown || middleBtnDown || secondaryBtnDown || backBtnDown || forwardBtnDown; } else if (e.getID() == MouseEvent.MOUSE_CLICKED) { // Don't send click events to FX, as they are generated in Scene return; } // A workaround until JDK-8065131 is fixed. boolean popupTrigger = false; if (e.getID() == MouseEvent.MOUSE_PRESSED || e.getID() == MouseEvent.MOUSE_RELEASED) { popupTrigger = e.isPopupTrigger(); } if(e.getID() == MouseEvent.MOUSE_WHEEL) { scenePeer.scrollEvent(AbstractEvents.MOUSEEVENT_VERTICAL_WHEEL, 0, -SwingEvents.getWheelRotation(e), 0, 0, // total scroll 40, 40, // multiplier e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), (extModifiers & MouseEvent.SHIFT_DOWN_MASK) != 0, (extModifiers & MouseEvent.CTRL_DOWN_MASK) != 0, (extModifiers & MouseEvent.ALT_DOWN_MASK) != 0, (extModifiers & MouseEvent.META_DOWN_MASK) != 0, false); } else { scenePeer.mouseEvent( SwingEvents.mouseIDToEmbedMouseType(e.getID()), SwingEvents.mouseButtonToEmbedMouseButton(e.getButton(), extModifiers), primaryBtnDown, middleBtnDown, secondaryBtnDown, backBtnDown, forwardBtnDown, e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), (extModifiers & MouseEvent.SHIFT_DOWN_MASK) != 0, (extModifiers & MouseEvent.CTRL_DOWN_MASK) != 0, (extModifiers & MouseEvent.ALT_DOWN_MASK) != 0, (extModifiers & MouseEvent.META_DOWN_MASK) != 0, popupTrigger); } if (e.isPopupTrigger()) { scenePeer.menuEvent(e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), false); } }
Overrides the Component.processMouseEvent(MouseEvent) method to dispatch the mouse event to the JavaFX scene attached to this JFXPanel.
Params:
  • e – the mouse event to dispatch to the JavaFX scene
/** * Overrides the {@link java.awt.Component#processMouseEvent(MouseEvent)} * method to dispatch the mouse event to the JavaFX scene attached to this * {@code JFXPanel}. * * @param e the mouse event to dispatch to the JavaFX scene */
@Override protected void processMouseEvent(MouseEvent e) { if ((e.getID() == MouseEvent.MOUSE_PRESSED) && (e.getButton() == MouseEvent.BUTTON1)) { if (isFocusable() && !hasFocus()) { requestFocus(); // this focus request event goes to eventqueue and will be // asynchronously handled so MOUSE_PRESSED event will not be // honoured by FX immediately due to lack of focus in fx // component. Fire the same MOUSE_PRESSED event after // requestFocus() so that 2nd mouse press will be honoured // since now fx have focus jfxPanelIOP.postEvent(this, e); } } sendMouseEventToFX(e); super.processMouseEvent(e); }
Overrides the Component.processMouseMotionEvent(MouseEvent) method to dispatch the mouse motion event to the JavaFX scene attached to this JFXPanel.
Params:
  • e – the mouse motion event to dispatch to the JavaFX scene
/** * Overrides the {@link java.awt.Component#processMouseMotionEvent(MouseEvent)} * method to dispatch the mouse motion event to the JavaFX scene attached to * this {@code JFXPanel}. * * @param e the mouse motion event to dispatch to the JavaFX scene */
@Override protected void processMouseMotionEvent(MouseEvent e) { sendMouseEventToFX(e); super.processMouseMotionEvent(e); }
Overrides the Component.processMouseWheelEvent(MouseWheelEvent) method to dispatch the mouse wheel event to the JavaFX scene attached to this JFXPanel.
Params:
  • e – the mouse wheel event to dispatch to the JavaFX scene
/** * Overrides the * {@link java.awt.Component#processMouseWheelEvent(MouseWheelEvent)} * method to dispatch the mouse wheel event to the JavaFX scene attached * to this {@code JFXPanel}. * * @param e the mouse wheel event to dispatch to the JavaFX scene */
@Override protected void processMouseWheelEvent(MouseWheelEvent e) { sendMouseEventToFX(e); super.processMouseWheelEvent(e); } private void sendKeyEventToFX(final KeyEvent e) { if (scenePeer == null || !isFxEnabled()) { return; } char[] chars = (e.getKeyChar() == KeyEvent.CHAR_UNDEFINED) ? new char[] {} : new char[] { SwingEvents.keyCharToEmbedKeyChar(e.getKeyChar()) }; scenePeer.keyEvent( SwingEvents.keyIDToEmbedKeyType(e.getID()), e.getKeyCode(), chars, SwingEvents.keyModifiersToEmbedKeyModifiers(e.getModifiersEx())); }
Overrides the Component.processKeyEvent(KeyEvent) method to dispatch the key event to the JavaFX scene attached to this JFXPanel.
Params:
  • e – the key event to dispatch to the JavaFX scene
/** * Overrides the {@link java.awt.Component#processKeyEvent(KeyEvent)} * method to dispatch the key event to the JavaFX scene attached to this * {@code JFXPanel}. * * @param e the key event to dispatch to the JavaFX scene */
@Override protected void processKeyEvent(KeyEvent e) { sendKeyEventToFX(e); super.processKeyEvent(e); } private void sendResizeEventToFX() { if (stagePeer != null) { stagePeer.setSize(pWidth, pHeight); } if (scenePeer != null) { scenePeer.setSize(pWidth, pHeight); } }
Overrides the Component.processComponentEvent(ComponentEvent) method to dispatch ComponentEvent.COMPONENT_RESIZED events to the JavaFX scene attached to this JFXPanel. The JavaFX scene object is then resized to match the JFXPanel size.
Params:
  • e – the component event to dispatch to the JavaFX scene
/** * Overrides the * {@link java.awt.Component#processComponentEvent(ComponentEvent)} * method to dispatch {@link java.awt.event.ComponentEvent#COMPONENT_RESIZED} * events to the JavaFX scene attached to this {@code JFXPanel}. The JavaFX * scene object is then resized to match the {@code JFXPanel} size. * * @param e the component event to dispatch to the JavaFX scene */
@Override protected void processComponentEvent(ComponentEvent e) { switch (e.getID()) { case ComponentEvent.COMPONENT_RESIZED: { updateComponentSize(); break; } case ComponentEvent.COMPONENT_MOVED: { if (updateScreenLocation()) { sendMoveEventToFX(); } break; } default: { break; } } super.processComponentEvent(e); } // called on EDT only private void updateComponentSize() { int oldWidth = pWidth; int oldHeight = pHeight; // It's quite possible to get negative values here, this is not // what JavaFX embedded scenes/stages are ready to pWidth = Math.max(0, getWidth()); pHeight = Math.max(0, getHeight()); if (getBorder() != null) { Insets i = getBorder().getBorderInsets(this); pWidth -= (i.left + i.right); pHeight -= (i.top + i.bottom); } double newScaleFactorX = scaleFactorX; double newScaleFactorY = scaleFactorY; Graphics g = getGraphics(); newScaleFactorX = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(). getDefaultTransform().getScaleX(); newScaleFactorY = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(). getDefaultTransform().getScaleY(); if (oldWidth != pWidth || oldHeight != pHeight || newScaleFactorX != scaleFactorX || newScaleFactorY != scaleFactorY) { createResizePixelBuffer(newScaleFactorX, newScaleFactorY); if (scenePeer != null) { scenePeer.setPixelScaleFactors((float) newScaleFactorX, (float) newScaleFactorY); } scaleFactorX = newScaleFactorX; scaleFactorY = newScaleFactorY; sendResizeEventToFX(); } } // This methods should only be called on EDT private boolean updateScreenLocation() { synchronized (getTreeLock()) { if (isShowing()) { Point p = getLocationOnScreen(); screenX = p.x; screenY = p.y; return true; } } return false; } private void sendMoveEventToFX() { if (stagePeer == null) { return; } stagePeer.setLocation(screenX, screenY); }
Overrides the Component.processHierarchyBoundsEvent(HierarchyEvent) method to process HierarchyEvent.ANCESTOR_MOVED events and update the JavaFX scene location to match the JFXPanel location on the screen.
Params:
  • e – the hierarchy bounds event to process
/** * Overrides the * {@link java.awt.Component#processHierarchyBoundsEvent(HierarchyEvent)} * method to process {@link java.awt.event.HierarchyEvent#ANCESTOR_MOVED} * events and update the JavaFX scene location to match the {@code * JFXPanel} location on the screen. * * @param e the hierarchy bounds event to process */
@Override protected void processHierarchyBoundsEvent(HierarchyEvent e) { if (e.getID() == HierarchyEvent.ANCESTOR_MOVED) { if (updateScreenLocation()) { sendMoveEventToFX(); } } super.processHierarchyBoundsEvent(e); } @Override protected void processHierarchyEvent(HierarchyEvent e) { if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) { if (updateScreenLocation()) { sendMoveEventToFX(); } } super.processHierarchyEvent(e); } private void sendFocusEventToFX(final FocusEvent e) { if ((stage == null) || (stagePeer == null) || !isFxEnabled()) { return; } boolean focused = (e.getID() == FocusEvent.FOCUS_GAINED); int focusCause = (focused ? AbstractEvents.FOCUSEVENT_ACTIVATED : AbstractEvents.FOCUSEVENT_DEACTIVATED); if (focused) { if (e.getCause() == FocusEvent.Cause.TRAVERSAL_FORWARD) { focusCause = AbstractEvents.FOCUSEVENT_TRAVERSED_FORWARD; } else if (e.getCause() == FocusEvent.Cause.TRAVERSAL_BACKWARD) { focusCause = AbstractEvents.FOCUSEVENT_TRAVERSED_BACKWARD; } } stagePeer.setFocused(focused, focusCause); }
Overrides the Component.processFocusEvent(FocusEvent) method to dispatch focus events to the JavaFX scene attached to this JFXPanel.
Params:
  • e – the focus event to dispatch to the JavaFX scene
/** * Overrides the * {@link java.awt.Component#processFocusEvent(FocusEvent)} * method to dispatch focus events to the JavaFX scene attached to this * {@code JFXPanel}. * * @param e the focus event to dispatch to the JavaFX scene */
@Override protected void processFocusEvent(FocusEvent e) { sendFocusEventToFX(e); super.processFocusEvent(e); } // called on EDT only private void createResizePixelBuffer(double newScaleFactorX, double newScaleFactorY) { if (scenePeer == null || pWidth <= 0 || pHeight <= 0) { pixelsIm = null; } else { BufferedImage oldIm = pixelsIm; int newPixelW = (int) Math.ceil(pWidth * newScaleFactorX); int newPixelH = (int) Math.ceil(pHeight * newScaleFactorY); pixelsIm = new BufferedImage(newPixelW, newPixelH, SwingFXUtils.getBestBufferedImageType( scenePeer.getPixelFormat(), null, false)); if (oldIm != null) { double ratioX = newScaleFactorX / scaleFactorX; double ratioY = newScaleFactorY / scaleFactorY; // Transform old size to the new coordinate space. int oldW = (int)Math.round(oldIm.getWidth() * ratioX); int oldH = (int)Math.round(oldIm.getHeight() * ratioY); Graphics g = pixelsIm.getGraphics(); try { g.drawImage(oldIm, 0, 0, oldW, oldH, null); } finally { g.dispose(); } } } } @Override protected void processInputMethodEvent(InputMethodEvent e) { if (e.getID() == InputMethodEvent.INPUT_METHOD_TEXT_CHANGED) { sendInputMethodEventToFX(e); } super.processInputMethodEvent(e); } private void sendInputMethodEventToFX(InputMethodEvent e) { String t = InputMethodSupport.getTextForEvent(e); int insertionIndex = 0; if (e.getCaret() != null) { insertionIndex = e.getCaret().getInsertionIndex(); } scenePeer.inputMethodEvent( javafx.scene.input.InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, InputMethodSupport.inputMethodEventComposed(t, e.getCommittedCharacterCount()), t.substring(0, e.getCommittedCharacterCount()), insertionIndex); }
Overrides the JComponent.paintComponent(Graphics) method to paint the content of the JavaFX scene attached to this JFXpanel.
Params:
  • g – the Graphics context in which to paint
See Also:
/** * Overrides the {@link javax.swing.JComponent#paintComponent(Graphics)} * method to paint the content of the JavaFX scene attached to this * {@code JFXpanel}. * * @param g the Graphics context in which to paint * * @see #isOpaque() */
@Override protected void paintComponent(Graphics g) { if (scenePeer == null) { return; } if (pixelsIm == null) { createResizePixelBuffer(scaleFactorX, scaleFactorY); if (pixelsIm == null) { return; } } DataBufferInt dataBuf = (DataBufferInt)pixelsIm.getRaster().getDataBuffer(); int[] pixelsData = dataBuf.getData(); IntBuffer buf = IntBuffer.wrap(pixelsData); if (!scenePeer.getPixels(buf, pWidth, pHeight)) { // In this case we just render what we have so far in the buffer. } Graphics gg = null; try { gg = g.create(); if ((opacity < 1.0f) && (gg instanceof Graphics2D)) { Graphics2D g2d = (Graphics2D)gg; AlphaComposite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity); g2d.setComposite(c); } if (getBorder() != null) { Insets i = getBorder().getBorderInsets(this); gg.translate(i.left, i.top); } gg.drawImage(pixelsIm, 0, 0, pWidth, pHeight, null); double newScaleFactorX = scaleFactorX; double newScaleFactorY = scaleFactorY; newScaleFactorX = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(). getDefaultTransform().getScaleX(); newScaleFactorY = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(). getDefaultTransform().getScaleY(); if (scaleFactorX != newScaleFactorX || scaleFactorY != newScaleFactorY) { createResizePixelBuffer(newScaleFactorX, newScaleFactorY); // The scene will request repaint. scenePeer.setPixelScaleFactors((float) newScaleFactorX, (float) newScaleFactorY); scaleFactorX = newScaleFactorX; scaleFactorY = newScaleFactorY; } } catch (Throwable th) { th.printStackTrace(); } finally { if (gg != null) { gg.dispose(); } } }
Returns the preferred size of this JFXPanel, either previously set with JComponent.setPreferredSize(Dimension) or based on the content of the JavaFX scene attached to this JFXPanel.
Returns:prefSize this JFXPanel preferred size
/** * Returns the preferred size of this {@code JFXPanel}, either * previously set with {@link #setPreferredSize(Dimension)} or * based on the content of the JavaFX scene attached to this {@code * JFXPanel}. * * @return prefSize this {@code JFXPanel} preferred size */
@Override public Dimension getPreferredSize() { if (isPreferredSizeSet() || scenePeer == null) { return super.getPreferredSize(); } return new Dimension(pPreferredWidth, pPreferredHeight); } private boolean isFxEnabled() { return this.disableCount.get() == 0; } private void setFxEnabled(boolean enabled) { if (!enabled) { if (disableCount.incrementAndGet() == 1) { if (dnd != null) { dnd.removeNotify(); } } } else { if (disableCount.get() == 0) { //should report a warning about an extra enable call ? return; } if (disableCount.decrementAndGet() == 0) { if (dnd != null) { dnd.addNotify(); } } } } private transient AWTEventListener ungrabListener = event -> { if (jfxPanelIOP.isUngrabEvent(event)) { SwingNodeHelper.runOnFxThread(() -> { if (JFXPanel.this.stagePeer != null && getScene() != null && getScene().getFocusOwner() != null && getScene().getFocusOwner().isFocused()) { JFXPanel.this.stagePeer.focusUngrab(); } }); } if (event instanceof MouseEvent) { // Synthesize FOCUS_UNGRAB if user clicks the AWT top-level window // that contains the JFXPanel. if (event.getID() == MouseEvent.MOUSE_PRESSED && event.getSource() instanceof Component) { final Window jfxPanelWindow = SwingUtilities.getWindowAncestor(JFXPanel.this); final Component source = (Component)event.getSource(); final Window eventWindow = source instanceof Window ? (Window)source : SwingUtilities.getWindowAncestor(source); if (jfxPanelWindow == eventWindow) { SwingNodeHelper.runOnFxThread(() -> { if (JFXPanel.this.stagePeer != null) { // No need to check if grab is active or not. // NoAutoHide popups don't request the grab and // ignore the Ungrab event anyway. // AutoHide popups actually should be hidden when // user clicks some non-FX content, even if for // some reason they didn't install the grab when // they were shown. JFXPanel.this.stagePeer.focusUngrab(); } }); } } } };
Notifies this component that it now has a parent component. When this method is invoked, the chain of parent components is set up with KeyboardAction event listeners.
/** * Notifies this component that it now has a parent component. When this * method is invoked, the chain of parent components is set up with * KeyboardAction event listeners. */
@Override public void addNotify() { super.addNotify(); registerFinishListener(); AccessController.doPrivileged((PrivilegedAction<Void>) () -> { JFXPanel.this.getToolkit().addAWTEventListener(ungrabListener, jfxPanelIOP.getMask()); return null; }); updateComponentSize(); // see RT-23603 SwingNodeHelper.runOnFxThread(() -> { if ((stage != null) && !stage.isShowing()) { stage.show(); sendMoveEventToFX(); } }); } @Override public InputMethodRequests getInputMethodRequests() { EmbeddedSceneInterface scene = scenePeer; if (scene == null) { return null; } return new InputMethodSupport.InputMethodRequestsAdapter(scene.getInputMethodRequests()); }
Notifies this component that it no longer has a parent component. When this method is invoked, any KeyboardActions set up in the the chain of parent components are removed.
/** * Notifies this component that it no longer has a parent component. * When this method is invoked, any KeyboardActions set up in the the * chain of parent components are removed. */
@Override public void removeNotify() { SwingNodeHelper.runOnFxThread(() -> { if ((stage != null) && stage.isShowing()) { stage.hide(); } }); pixelsIm = null; pWidth = 0; pHeight = 0; super.removeNotify(); AccessController.doPrivileged((PrivilegedAction<Void>) () -> { JFXPanel.this.getToolkit().removeAWTEventListener(ungrabListener); return null; }); /* see CR 4867453 */ getInputContext().removeNotify(this); deregisterFinishListener(); } private void invokeOnClientEDT(Runnable r) { jfxPanelIOP.postEvent(this, new InvocationEvent(this, r)); } private class HostContainer implements HostInterface { @Override public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) { stagePeer = embeddedStage; if (stagePeer == null) { return; } if (pWidth > 0 && pHeight > 0) { stagePeer.setSize(pWidth, pHeight); } invokeOnClientEDT(() -> { if (stagePeer != null && JFXPanel.this.isFocusOwner()) { stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED); } }); sendMoveEventToFX(); } @Override public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) { if (scenePeer == embeddedScene) { return; } scenePeer = embeddedScene; if (scenePeer == null) { invokeOnClientEDT(() -> { if (dnd != null) { dnd.removeNotify(); dnd = null; } }); return; } if (pWidth > 0 && pHeight > 0) { scenePeer.setSize(pWidth, pHeight); } scenePeer.setPixelScaleFactors((float) scaleFactorX, (float) scaleFactorY); invokeOnClientEDT(() -> { dnd = new SwingDnD(JFXPanel.this, scenePeer); dnd.addNotify(); if (scenePeer != null) { scenePeer.setDragStartListener(dnd.getDragStartListener()); } }); } @Override public boolean requestFocus() { return requestFocusInWindow(); } @Override public boolean traverseFocusOut(boolean forward) { KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); if (forward) { kfm.focusNextComponent(JFXPanel.this); } else { kfm.focusPreviousComponent(JFXPanel.this); } return true; } @Override public void setPreferredSize(final int width, final int height) { invokeOnClientEDT(() -> { JFXPanel.this.pPreferredWidth = width; JFXPanel.this.pPreferredHeight = height; JFXPanel.this.revalidate(); }); } @Override public void repaint() { invokeOnClientEDT(() -> { JFXPanel.this.repaint(); }); } @Override public void setEnabled(final boolean enabled) { JFXPanel.this.setFxEnabled(enabled); } @Override public void setCursor(CursorFrame cursorFrame) { final Cursor cursor = getPlatformCursor(cursorFrame); invokeOnClientEDT(() -> { JFXPanel.this.setCursor(cursor); }); } private Cursor getPlatformCursor(final CursorFrame cursorFrame) { final Cursor cachedPlatformCursor = cursorFrame.getPlatformCursor(Cursor.class); if (cachedPlatformCursor != null) { // platform cursor already cached return cachedPlatformCursor; } // platform cursor not cached yet final Cursor platformCursor = SwingCursors.embedCursorToCursor(cursorFrame); cursorFrame.setPlatforCursor(Cursor.class, platformCursor); return platformCursor; } @Override public boolean grabFocus() { // On X11 grab is limited to a single XDisplay connection, // so we can't delegate it to another GUI toolkit. if (PlatformUtil.isLinux()) return true; invokeOnClientEDT(() -> { Window window = SwingUtilities.getWindowAncestor(JFXPanel.this); if (window != null) { jfxPanelIOP.grab(JFXPanel.this.getToolkit(), window); } }); return true; // Oh, well... } @Override public void ungrabFocus() { // On X11 grab is limited to a single XDisplay connection, // so we can't delegate it to another GUI toolkit. if (PlatformUtil.isLinux()) return; invokeOnClientEDT(() -> { Window window = SwingUtilities.getWindowAncestor(JFXPanel.this); if (window != null) { jfxPanelIOP.ungrab(JFXPanel.this.getToolkit(), window); } }); } } }