概要

JPopupMenuの親のJWindowJMenuJMenuItemなどを透明にして、JPopupMenuを半透明にします。

サンプルコード

class TransparentMenu extends JMenu {
  private JPopupMenu popupMenu;
  public TransparentMenu(String title) {
    super(title);
  }

  // https://bugs.openjdk.org/browse/JDK-4688783
  private void ensurePopupMenuCreated() {
    if (popupMenu == null) {
      this.popupMenu = new TranslucentPopupMenu();
      popupMenu.setInvoker(this);
      popupListener = createWinListener(popupMenu);
    }
  }

  @Override public JPopupMenu getPopupMenu() {
    ensurePopupMenuCreated();
    return popupMenu;
  }

  @Override public JMenuItem add(JMenuItem menuItem) {
    ensurePopupMenuCreated();
    menuItem.setOpaque(false);
    return popupMenu.add(menuItem);
  }

  @Override public Component add(Component c) {
    ensurePopupMenuCreated();
    if (c instanceof JComponent) {
      ((JComponent) c).setOpaque(false);
    }
    popupMenu.add(c);
    return c;
  }

  @Override public void addSeparator() {
    ensurePopupMenuCreated();
    popupMenu.addSeparator();
  }

  @Override public void insert(String s, int pos) {
    if (pos < 0) {
      throw new IllegalArgumentException("index less than zero.");
    }
    ensurePopupMenuCreated();
    popupMenu.insert(new JMenuItem(s), pos);
  }

  @Override public JMenuItem insert(JMenuItem mi, int pos) {
    if (pos < 0) {
      throw new IllegalArgumentException("index less than zero.");
    }
    ensurePopupMenuCreated();
    popupMenu.insert(mi, pos);
    return mi;
  }

  @Override public void insertSeparator(int index) {
    if (index < 0) {
      throw new IllegalArgumentException("index less than zero.");
    }
    ensurePopupMenuCreated();
    popupMenu.insert(new JPopupMenu.Separator(), index);
  }

  @Override public boolean isPopupMenuVisible() {
    ensurePopupMenuCreated();
    return popupMenu.isVisible();
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、JMenuを継承するTransparentMenuを作成してJMenu自身と子のJMenuItemなどを透明化しています。またJMenuから開くJPopupMenuJPopupMenuを半透明にするを使用して半透明になるよう設定しています。

PopupFactory.setSharedInstance(new TranslucentPopupFactory());
// ...
class TranslucentPopupFactory extends PopupFactory {
  @Override public Popup getPopup(Component owner, Component contents, int x, int y)
                                                   throws IllegalArgumentException {
     return new TranslucentPopup(owner, contents, x, y);
   }
}

  • JPopupMenuを半透明にするでは、JPopupMenu#show(...)メソッドをオーバーライドすることでポップアップが親フレームからはみ出してHeavy weightJWindowJPopupMenuの親となる場合のみJWindow#setBackground(ALPHA_ZERO)などで透明化(JPopupMenuは半透明)
----
HeavyWeightWindow: win0, JPopupMenu: base
javax.swing.JPanel: false
javax.swing.JLayeredPane: false
javax.swing.JRootPane: false
----
HeavyWeightWindow: win1, JPopupMenu: sub
javax.swing.JPanel: true
javax.swing.JLayeredPane: false
javax.swing.JRootPane: true
class TranslucentPopupMenu extends JPopupMenu {
  @Override public void show(Component c, int x, int y) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        Window p = SwingUtilities.getWindowAncestor(TranslucentPopupMenu.this);
        if (p != null && p instanceof JWindow) {
          JWindow w = (JWindow) p;
          w.setBackground(ALPHA_ZERO);
          System.out.format("HeavyWeightWindow: %s, JPopupMenu: %s\n", w.getName(), getName());
          Container c = (Container) w.getContentPane();
          while (c != null && c instanceof JComponent) {
            JComponent jc = (JComponent) c;
            System.out.format("%s: %s\n", c.getClass().getName(), jc.isOpaque());
            if (jc.isOpaque()) {
              jc.setOpaque(false);
            }
            c = c.getParent();
          }
        } else {
          System.out.println("Light weight");
        }
      }
    });
    super.show(c, x, y);
  }
  @Override protected void paintComponent(Graphics g) {
  // ...

参考リンク

コメント