---
category: swing
folder: MenuBarOverflowMenu
title: JMenuBarからあふれたJMenuをオーバーフローメニューに移動する
tags: [JMenu, JMenuBar, JPopupMenu, FlowLayout]
author: aterai
pubdate: 2025-08-18T01:13:09+09:00
description: JMenuBarの表示幅に収まらずにあふれてしまうJMenuを新規作成したオーバーフロー用メニューに移動して表示可能にします。
image: https://drive.google.com/uc?id=1awkuBPpsKphzVEF-qzWtIGBO9Ec8rdh7
---
* Summary [#summary]
`JMenuBar`の表示幅に収まらずにあふれてしまう`JMenu`を新規作成したオーバーフロー用メニューに移動して表示可能にします。

#download(https://drive.google.com/uc?id=1awkuBPpsKphzVEF-qzWtIGBO9Ec8rdh7)

* Source Code Examples [#sourcecode]
#code(link){{
class OverflowMenuLayout extends FlowLayout {
  public final JMenu overflowMenu = new JMenu("...");

  protected OverflowMenuLayout() {
    super(LEADING, 0, 0);
  }

  @Override public void layoutContainer(Container target) {
    super.layoutContainer(target);
    Rectangle r = SwingUtilities.calculateInnerArea((JComponent) target, null);
    int num = target.getComponentCount();
    if (target.getComponent(num - 1).getY() > r.y + getVgap()) {
      target.add(overflowMenu);
      overflowMenu.setSize(overflowMenu.getPreferredSize());
      int popupX;
      if (target.getComponentOrientation().isLeftToRight()) {
        popupX = r.x + r.width - overflowMenu.getSize().width;
      } else {
        popupX = r.x;
      }
      overflowMenu.setLocation(popupX, r.y);
      overflowMenu.setVisible(true);
    }
    if (target.isAncestorOf(overflowMenu)) {
      Arrays.stream(target.getComponents())
          .filter(c -> shouldMoveToPopup(target, c))
          .forEach(overflowMenu::add);
      // This is not necessary in Java 8, but if you do not do this in Java 21,
      // `getPopupMenu().pack()` is not necessary in Java 8,
      // but if you do not do this in Java 21,
      // the JPopupMenu may not be displayed correctly.
      overflowMenu.getPopupMenu().pack();
    }
  }

  private boolean shouldMoveToPopup(Container target, Component c) {
    Insets insets = target.getInsets();
    int y = insets.top + getVgap();
    Point pt = overflowMenu.getLocation();
    if (!target.getComponentOrientation().isLeftToRight()) {
      pt.x += overflowMenu.getWidth();
    }
    boolean b = !Objects.equals(c, overflowMenu) && c.getBounds().contains(pt);
    return c.getY() > y || b;
  }

  @Override public Dimension preferredLayoutSize(Container target) {
    overflowMenu.setVisible(false);
    target.remove(overflowMenu);
    for (Component c : overflowMenu.getPopupMenu().getComponents()) {
      target.add(c);
    }
    overflowMenu.removeAll();
    return super.preferredLayoutSize(target);
  }
}
}}

* Description [#description]
- `JMenuBar`のレイアウトを`FlowLayout`を継承する`LayoutManager`に変更
-- `FlowLayout#layoutContainer(...)`をオーバーライドして、`JMenu`の`y`座標を調査して`2`行目に配置されている場合はあふれているとみなし、その`JMenu`を`JMenuBar`からオーバーフロー用`JMenu`に移動
-- または、`JMenu`があふれていない場合でもその領域が`JMenuBar`の右端(`ComponentOrientation.RIGHT_TO_LEFT`の場合は左端)に追加したオーバーフロー用`JMenu`と重なる場合はオーバーフロー用`JMenu`に移動
--- `Java 21` + `Windows 10`環境では、あふれた`JMenu`をすべて移動した後に`overflowMenu.getPopupMenu().pack()`を実行しないとオーバーフロー用`JMenu`から開く`JPopupMenu`の表示がおかしくなる場合がある(`JMenuItem`のテキストなどが重なったり途切れたりする)
--- `Java 8` + `Windows 10`環境では、`pack()`しなくても特に問題なく表示可能
- `LookAndFeel`を変更したときオーバーフロー用`JMenu`が`JMenuBar`に配置されていない場合でも`UI`を更新するよう、以下のように`SwingUtilities.updateComponentTreeUI(menu)`を実行する必要がある
#code{{
JMenuBar menuBar = new JMenuBar() {
  @Override public void updateUI() {
    super.updateUI();
    LayoutManager lm = getLayout();
    if (lm instanceof OverflowMenuLayout) {
      JMenu menu = ((OverflowMenuLayout) lm).overflowMenu;
      SwingUtilities.updateComponentTreeUI(menu);
    }
  }
};
menuBar.setLayout(new OverflowMenuLayout());
}}


* Reference [#reference]
- [https://code.visualstudio.com/updates/v1_30#_menu-bar-overflow-menu Menu bar overflow menu - November 2018 (version 1.30) - Visual Studio Code UPDATES]
- [[JMenuBarのJMenuを折り返し>Swing/MenuBarLayout]]

* Comment [#comment]
#comment
#comment