2016-08-24 3 views
0

Я пытаюсь внедрить стороннее приложение JavaFX в более крупное приложение Swing. Требование состоит в том, что он ведет себя так, как будто это немодальное дочернее окно. У меня вроде как эта работа помогла How to open modal dialog from JFXPanel in JavaFX?.Открыть дочерний диалог JavaFX от JFXPanel в JFrame

Однако порядок заказа окон не установлен правильно. Можно поместить дочерний этап позади родительского JFrame. Я бы не ожидал этого с дочерним окном.

Использование xprop показывает в Ubuntu 16.04, что X11 Atom WM_TRANSIENT_FOR устанавливается только для ребенка JDialog, а не ребенок Stage

Пример - приложение, которое открывает два дочерних окон. Один JavaFX и один Swing. Качели правильно правильно. 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); 
    } 
} 

ответ

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

Затем, «принять» окно, добавив переходный атом

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, в противном случае обработчик ошибок по умолчанию вызывает сбои виртуальной машины, это происходит при попытке получить информацию об окнах, которые исчезли с момента первоначального обнаружения.

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; 
     } 
    }); 
Смежные вопросы