概要

JMenuを選択して開くJPopupMenuが親ウィンドウ内に表示されるよう位置を変更します。

サンプルコード

JMenu menu = new JMenu(key) {
  @Override public void setPopupMenuVisible(boolean b) {
    if (isTopLevelMenu()) {
      Point p = getLocation();
      Rectangle r = getRootPane().getBounds();
      Dimension d1 = getPopupMenu().getPreferredSize();
      if (p.x + d1.width > r.width) {
        Dimension d2 = getPreferredSize();
        setMenuLocation(d2.width - d1.width, d2.height);
      }
    }
    super.setPopupMenuVisible(b);
  }
};
View in GitHub: Java, Kotlin

解説

  • JMenuJMenuBarに配置されたTopLevelMenuJMenuを選択して表示されるJPopupMenuが親Window領域外に配置される場合、オーバーライドしたJMenu#setPopupMenuVisible(...)内でJMenu#setMenuLocation(...)を使用して親Window領域内に収まるようJPopupMenuの位置を変更
  • TopLevelMenuの場合のJPopupMenuの表示位置オフセットはUIManager.put("Menu.menuPopupOffsetX", offset)UIManager.put("Menu.menuPopupOffsetY", offset)で変更可能
  • TopLevelMenuではない場合のJPopupMenuの表示位置オフセットはUIManager.put("Menu.submenuPopupOffsetX", offset)UIManager.put("Menu.submenuPopupOffsetY", offset)で変更可能
  • JMenuTopLevelMenuではなくサブメニューの場合、同じくオーバーライドしたJMenu#setPopupMenuVisible(...)内でJMenu#setMenuLocation(...)を使用して親JPopupMenuと同じ位置に同じサイズでJPopupMenuを表示するよう位置を変更
    • WindowsLookAndFeelの場合親JPopupMenuのハイライトが手前に残る場合がある?
    • テストとしてJMenu内にカーソルが入ったときではなくクリックイベントでJPopupMenuを開閉するようJMenu#setDelay(...)100秒の遅延時間を設定しているがおそらく修正が必要になりそう
JMenu menu = new JMenu(title) {
  @Override public void setPopupMenuVisible(boolean b) {
    JPopupMenu popup = getPopupMenu();
    popup.setPopupSize(getParent().getPreferredSize());
    Point p = getLocation();
    setMenuLocation(-p.x, -p.y);
    super.setPopupMenuVisible(b);
  }

  @Override public JMenuItem add(JMenuItem item) {
    item.setMaximumSize(new Dimension(Short.MAX_VALUE, item.getPreferredSize().height));
    return super.add(item);
  }
};

参考リンク

コメント