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); // 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