JMenuBarからあふれたJMenuをオーバーフローメニューに移動する
Total: 17
, Today: 17
, Yesterday: 0
Posted by aterai at
Last-modified:
Summary
JMenuBar
の表示幅に収まらずにあふれてしまうJMenu
を新規作成したオーバーフロー用メニューに移動して表示可能にします。
Screenshot

Advertisement
Source Code Examples
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);
}
}
View in GitHub: Java, KotlinDescription
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)
を実行する必要がある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
- Menu bar overflow menu - November 2018 (version 1.30) - Visual Studio Code UPDATES
- JMenuBarのJMenuを折り返し