Swing/FilterPopupMenuItems の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/FilterPopupMenuItems へ行く。
- Swing/FilterPopupMenuItems の差分を削除
--- category: swing folder: FilterPopupMenuItems title: JPopupMenuに追加したJTextFieldを使用してJMenuItemの表示をフィルタリングする tags: [JPopupMenu, JMenu, JMenuItem, JTextField, DocumentListener] author: aterai pubdate: 2025-09-08T01:34:51+09:00 description: JPopupMenuの先頭にJTextFieldを配置し入力された正規表現パターンを使用して、同じくJPopupMenu内に配置されたJMenuItemの表示状態をフィルタリングします。 image: https://drive.google.com/uc?id=1azsXn2VAy9skJaGxbQL6qDiwBZiMz7Nb --- * Summary [#summary] `JPopupMenu`の先頭に`JTextField`を配置し入力された正規表現パターンを使用して、同じく`JPopupMenu`内に配置された`JMenuItem`の表示状態をフィルタリングします。 #download(https://drive.google.com/uc?id=1azsXn2VAy9skJaGxbQL6qDiwBZiMz7Nb) * Source Code Examples [#sourcecode] #code(link){{ private static JMenu makeRecentMenu() { JMenu menu = new JMenu("Recent Files"); JTextField field = new JTextField(20); menu.add(field); field.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { filter(menu, field); } @Override public void removeUpdate(DocumentEvent e) { filter(menu, field); } @Override public void changedUpdate(DocumentEvent e) { /* not needed */ } }); menu.add("aa001.txt"); menu.add("aa002.log"); menu.add("aabb33.txt"); menu.add("abc4.md"); menu.add("b5.markdown"); menu.add("ccc6.txt"); return menu; } private static Pattern getPattern(JTextField field) { String regex = field.getText(); Pattern pattern = null; if (Objects.nonNull(regex) && !regex.isEmpty()) { try { pattern = Pattern.compile(regex); } catch (PatternSyntaxException ex) { UIManager.getLookAndFeel().provideErrorFeedback(field); } } return pattern; } private static void filter(JMenu menu, JTextField field) { Pattern ptn = getPattern(field); Stream.of(menu.getPopupMenu().getSubElements()) .filter(JMenuItem.class::isInstance) .map(JMenuItem.class::cast) .forEach(mi -> mi.setVisible(ptn == null || ptn.matcher(mi.getText()).find())); menu.getPopupMenu().pack(); EventQueue.invokeLater(field::requestFocusInWindow); } }} * Description [#description] - `JMenu`(`JMenu`が使用する`JPopupMenu`)に正規表現`Pattern`入力用の`JTextField`を挿入 - `JMenu`(`JMenu`が使用する`JPopupMenu`)に正規表現`Pattern`を入力するための`JTextField`を挿入 -- [[JPopupMenuのMenuItemとしてJSpinnerを配置する>Swing/SpinnerMenuItem]]のように`JMenuItem`の子要素として`JSpinner`ではなく`JTextField`を使用すると`JTextField`にフォーカス移動不可となって編集できない? → 調査中 - `JTextField`の編集を`DocumentListener`で取得し、入力された正規表現`Pattern`にマッチするタイトルの`JMenuItem`を検索する -- `JMenu#getPopupMenu()#getSubElements()`で`JTextField`を除く`MenuElement`一覧を取得して`JMenuItem`のタイトルを調査 --- マッチする場合は`JMenuItem#setVisible(true)`で可視化、それ以外を`JMenuItem#setVisible(false)`で非表示化 --- [[JMenuBarからあふれたJMenuをオーバーフローメニューに移動する>Swing/MenuBarOverflowMenu]]のように`JMenuItem`を移動(追加、削除)する必要はないので、`JMenuItem#setVisible(...)`での表示・非表示の切り替えでフィルタリングを実行している - 表示される`JMenuItem`の数が変化するので、`JMenu#getPopupMenu()#pack()`を実行して親`JPopupMenu`のサイズを更新 -- `pack()`後に`EventQueue.invokeLater(field::requestFocusInWindow)`を実行して`JTextField`側にフォーカスを戻す * Reference [#reference] - [[JListのアイテムをフィルタリングして表示>Swing/FilterListItems]] - [[JPopupMenuに追加したJSliderの表示・非表示をチェックボックスで切り替える>Swing/SliderMenuItem]] - [[JPopupMenuのMenuItemとしてJSpinnerを配置する>Swing/SpinnerMenuItem]] - [[JMenuBarからあふれたJMenuをオーバーフローメニューに移動する>Swing/MenuBarOverflowMenu]] * Comment [#comment] #comment #comment