TITLE:JMenuなどから開くPopupMenuを半透明化
#navi(../)
RIGHT:Posted by [[aterai]] at 2012-10-22
*JMenuなどから開くPopupMenuを半透明化 [#ia005766]
JPopupMenuの親のJWindow、JMenuやJMenuItemなどを透明にして、JPopupMenuを半透明にします。

-&jnlp;
-&jar;
-&zip;

//#screenshot
#ref(https://lh5.googleusercontent.com/-MKRZgWcSrRw/UIT3NRGfX9I/AAAAAAAABUk/fOYdfJmIt4g/s800/TranslucentSubMenu.png)

**サンプルコード [#v7863f10]
#code(link){{
class TransparentMenu extends JMenu {
  public TransparentMenu(String title) {
    super(title);
  }
  //http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4688783
  private JPopupMenu popupMenu;
  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();
  }
}
}}

**解説 [#p928b03d]
上記のサンプルでは、JMenuを継承するTransparentMenuを作成して、自身とJMenuItemなどを透明化し、JMenuから開くJPopupMenuは、[[JPopupMenuを半透明にする>Swing/TranslucentPopupMenu]]を使用して半透明にしています。

- JPopupMenuの親の透明化
-- [http://today.java.net/pub/a/today/2008/03/18/translucent-and-shaped-swing-windows.html Translucent and Shaped Swing Windows | Java.net] を参考に PopupFactory#getPopup(...)をオーバーライドし、常にJPopupMenu(半透明)の親にJWindow(完全に透明、Heavy weight)を使用するように設定

#code{{
PopupFactory.setSharedInstance(new TranslucentPopupFactory());
}}

#code{{
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を半透明にする>Swing/TranslucentPopupMenu]]の場合は、JPopupMenu#show(...)メソッドをオーバーライドし、ポップアップが親フレームからはみ出して Heavy weightのJWindowが親として使用される時にだけ、JWindow#setBackground(ALPHA_ZERO)などで透明化(JPopupMenuは半透明)しているが、[http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7156657 Bug ID: 7156657 Version 7 doesn't support translucent popup menus against a translucent window] が原因?で、1.7.0_06 以前では、サブメニューが半透明化されない場合がある
--- PopupFactory.setSharedInstance(new TranslucentPopupFactory())では、バグの影響を受けない

**参考リンク [#s17a0f91]
- [http://today.java.net/pub/a/today/2008/03/18/translucent-and-shaped-swing-windows.html Translucent and Shaped Swing Windows | Java.net]
- [http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7156657 Bug ID: 7156657 Version 7 doesn't support translucent popup menus against a translucent window]
- [[JPopupMenuを半透明にする>Swing/TranslucentPopupMenu]]

**コメント [#iea685f4]
#comment