Swing/MenuBarOverflowMenu の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/MenuBarOverflowMenu へ行く。
- Swing/MenuBarOverflowMenu の差分を削除
---
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);
// `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(overflowMenu)`を実行する必要がある
#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]]
- [https://bugs.openjdk.org/browse/JDK-8365615 [JDK-8365615] Improve JMenuBar/RightLeftOrientation.java - Java Bug System]
* Comment [#comment]
#comment
#comment