• category: swing folder: FloatableMenuBar title: JToolBar内にJMenuBarを配置して分離・移動可能に設定する tags: [JToolBar, JMenuBarm, JMenu, JPopupMenu] author: aterai pubdate: 2023-03-20T21:13:17+09:00 description: JToolBar内にJMenuBarを配置することで分離・移動可能なメニューを作成します。 image: https://drive.google.com/uc?id=1DbHuFYcf7KnhBIFnBfL2wvZSudJvLGgd

概要

JToolBar内にJMenuBarを配置することで分離・移動可能なメニューを作成します。

サンプルコード

JMenuBar menuBar = new JMenuBar();
menuBar.add(makeMenu("JMenu 1"));

JMenu menu2 = makeMenu("JMenu 2");
menu2.addMenuListener(new MenuListener() {
  private boolean isFloating(JMenu menu) {
    Container c = SwingUtilities.getAncestorOfClass(JToolBar.class, menu);
    return c instanceof JToolBar &&
        !((BasicToolBarUI) ((JToolBar) c).getUI()).isFloating();
  }

  @Override public void menuSelected(MenuEvent e) {
    JMenu menu = (JMenu) e.getSource();
    if (menu.isTopLevelMenu() && isFloating(menu)) {
      Dimension d = menu.getPreferredSize();
      Point p = menu.getLocation();
      Component cp = getRootPane().getContentPane();
      Point pt = SwingUtilities.convertPoint(menu.getParent(), p, cp);
      pt.y += d.height * 2;
      if (!cp.getBounds().contains(pt)) {
        EventQueue.invokeLater(() -> {
          JPopupMenu popup = menu.getPopupMenu();
          Rectangle bounds = popup.getBounds();
          Point loc = menu.getLocationOnScreen();
          // int h = bounds.height + UIManager.getInt("Menu.menuPopupOffsetY");
          loc.y -= bounds.height + UIManager.getInt("Menu.menuPopupOffsetY");
          popup.setLocation(loc);
        });
      }
    }
  }

  @Override public void menuDeselected(MenuEvent e) {
    // Do nothing
  }

  @Override public void menuCanceled(MenuEvent e) {
    // Do nothing
  }
});
menuBar.add(menu2);

JToolBar toolBar = new JToolBar();
toolBar.setLayout(new BorderLayout());
toolBar.add(menuBar);

add(toolBar, BorderLayout.NORTH);
View in GitHub: Java, Kotlin

解説

  • JToolBarのレイアウトをBorderLayoutに変更してJMenuBarを追加
    • JToolBarのデフォルトレイアウトは配置で水平垂直を切り替えるBoxLayout
    • このBoxLayoutのままだとJMenuBarの幅がJToolBarの幅まで拡張されない
    • JMenuBar#setMaximumSize(...)で十分大きな最大サイズを設定すればBoxLayoutのままでも回避可能
    • このサンプルでは垂直にしてもレイアウトの切り替えは実行しないのでパネルの左右にJToolBarを配置不可に設定
  • MenuListenerJMenuに追加してBorderLayout.SOUTHJToolBarが配置されている場合はJMenuの上方にJPopupMenuを開くようその位置を変更
    • JMenuがトップレベル(親がJPopupMenuではなくJMenuBar)、かつJMenuBarがフローティング状態ではない場合のみ、JPopupMenuを開く位置を変更するかどうかチェックしている

参考リンク

コメント