在JFrame中从JFXPanel打开JavaFX子对话框

时间:2016-08-24 10:35:04

标签: java swing javafx javafx-8

我正在尝试在更大的Swing应用程序中嵌入第三方JavaFX应用程序。要求是它的行为就好像它是一个非模态子窗口。我有点帮助How to open modal dialog from JFXPanel in JavaFX?

但是窗口排序设置不正确。可以将子Stage放在父JFrame后面。我不希望这有一个子窗口。

使用xprop在Ubuntu 16.04中显示X11 Atom WM_TRANSIENT_FOR仅为子JDialog设置,而不是子Stage

示例 - 打开两个子窗口的应用程序。一个JavaFX和一个Swing。 Swing one是正确的父级。 JavaFX不是。

public class SwingApp {

    public static void main(String[] args) throws Exception {
        JFrame parent = new JFrame();
        parent.setTitle("Parent JFrame");
        parent.setSize(200, 150);
        JFXPanel jfxPanel = new JFXPanel();
        parent.getContentPane().setLayout(new BoxLayout(parent.getContentPane(), BoxLayout.Y_AXIS));
        JButton button = new JButton("Open Swing child");
        button.addActionListener(e -> {
            JDialog child = new JDialog(parent);
            child.setModal(false);
            child.getContentPane().add(new JLabel("content"));
            child.setVisible(true);
        });
        parent.getContentPane().add(button);
        parent.getContentPane().add(jfxPanel);
        parent.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        parent.setVisible(true);

        Platform.runLater(() -> jfxPanel.setScene(new Scene(createDummyFxApp(jfxPanel))));
    }

    private static Parent createDummyFxApp(JFXPanel openingPanel) {
        Button button = new Button("Open FX child");
        button.setOnAction(e -> {
            Stage stage = new Stage();
            stage.initModality(Modality.NONE);

            Window owner = openingPanel.getScene().getWindow();
            stage.initOwner(owner);

            stage.setTitle("Non-modal child JavaFX window");
            stage.setScene(new Scene(new HBox(new Label("content"))));
            stage.show();
        });

        return new HBox(button);
    }
}

1 个答案:

答案 0 :(得分:0)

我最终通过JNA实现了这一目标。首先按标题查找窗口。

public List<Window> find(Pattern title) {
    Display display = x11.XOpenDisplay(null);
    Window root = x11.XDefaultRootWindow(display);
    List<Window> windows = recurse(x11, display, root, title);
    x11.XCloseDisplay(display);
    return windows;
}

private synchronized List<Window> recurse(X11 x11, Display display, Window root, Pattern pattern) {
    List<Window> windows = new ArrayList<>(1);
    X11.WindowByReference windowRef = new X11.WindowByReference();
    X11.WindowByReference parentRef = new X11.WindowByReference();
    PointerByReference childrenRef = new PointerByReference();
    IntByReference childCountRef = new IntByReference();

    x11.XQueryTree(display, root, windowRef, parentRef, childrenRef, childCountRef);
    if (childrenRef.getValue() == null) {
        return Collections.emptyList();
    }

    long[] ids;

    if (Native.LONG_SIZE == Long.BYTES) {
        ids = childrenRef.getValue().getLongArray(0, childCountRef.getValue());
    } else if (Native.LONG_SIZE == Integer.BYTES) {
        int[] intIds = childrenRef.getValue().getIntArray(0, childCountRef.getValue());
        ids = new long[intIds.length];
        for (int i = 0; i < intIds.length; i++) {
            ids[i] = intIds[i];
        }
    } else {
        throw new IllegalStateException("Unexpected size for Native.LONG_SIZE" + Native.LONG_SIZE);
    }

    for (long id : ids) {
        Window child = new Window(id);
        X11.XTextProperty name = new X11.XTextProperty();
        int result = x11.XGetWMName(display, child, name);
        String value = name.value;
        LOGGER.info(String.format("Found window %s result: %d free %s", value, result, name));

        if (value != null && pattern.matcher(value).matches()) {
            windows.add(child);
        }
        windows.addAll(recurse(x11, display, child, pattern));
    }
    return windows;
}

然后,&#34;采用&#34;窗口通过添加瞬态原子

public void adopt(Window child, Window parent) {
    Display display = x11.XOpenDisplay(null);

    Atom wmState = x11.XInternAtom(display, "_NET_WM_STATE", false);
    Atom wmStateModal = x11.XInternAtom(display, "_NET_WM_STATE_MODAL", false);
    x11.XSetTransientForHint(display, child, parent);
    addAtom(display, child, wmState, wmStateModal);

    x11.XCloseDisplay(display);

}

使用此支持方法

public void addAtom(Display display, Window win, Atom key, Atom value) {

    int maskVal = X11.SubstructureRedirectMask | X11.SubstructureNotifyMask;
    NativeLong mask = new NativeLong(maskVal);

    XClientMessageEvent event = new X11.XClientMessageEvent();
    event.type = X11.ClientMessage;
    event.serial = new NativeLong(0);
    event.send_event = 1;
    event.message_type = key;
    event.window = win;
    event.format = 32;
    event.data.setType(NativeLong[].class);
    event.data.l[0] = new NativeLong(NET_WM_STATE_ADD);
    event.data.l[1] = value;
    event.data.l[2] = new NativeLong(0);
    event.data.l[3] = new NativeLong(0);
    event.data.l[4] = new NativeLong(0);

    X11.XEvent e = new X11.XEvent();
    e.setTypedValue(event);

    x11.XSendEvent(display, x11.XDefaultRootWindow(display), 0, mask, e);
    x11.XFlush(display);

}

还必须向X11添加错误处理程序,否则默认错误处理程序会崩溃VM,当尝试获取自原始发现后消失的窗口信息时会发生这种情况。

   x11.XSetErrorHandler(new XErrorHandler() {
        @Override
        public int apply(Display display, XErrorEvent errorEvent) {
            LOGGER.warn(String.format("X11 error during JNA, ignore for now: %s", errorEvent.toString()));
            return 0;
        }
    });