---
category: swing
folder: DisableComboPopupByVerifier
title: JComboBoxへのフォーカス移動がInputVerifierの検証で拒否されたらドロップダウンリスト表示をキャンセルする
tags: [JComboBox, InputVerifier, Focus, BasicComboPopup]
author: aterai
pubdate: 2022-05-23T00:52:07+09:00
description: JComboBoxへのマウスクリックによるフォーカス移動が別コンポーネントに設定されているInputVerifierの検証で拒否された場合、そのドロップダウンリストの表示をキャンセルします。
image: https://drive.google.com/uc?id=1xn-FG-x8N-VW-HcaW1ch4c-QcMoMkYzk
---
* 概要 [#summary]
`JComboBox`へのマウスクリックによるフォーカス移動が別コンポーネントに設定されている`InputVerifier`の検証で拒否された場合、そのドロップダウンリストの表示をキャンセルします。

#download(https://drive.google.com/uc?id=1xn-FG-x8N-VW-HcaW1ch4c-QcMoMkYzk)

* サンプルコード [#sourcecode]
#code(link){{
class BasicComboPopup2 extends BasicComboPopup {
  private transient MouseListener handler2;

  @Override public void uninstallingUI() {
    super.uninstallingUI();
    handler2 = null;
  }

  // Java 9: protected BasicComboPopup2(JComboBox<Object> combo) {
  protected BasicComboPopup2(JComboBox<?> combo) {
    super(combo);
  }

  @Override protected MouseListener createMouseListener() {
    if (Objects.isNull(handler2)) {
      handler2 = new Handler2();
    }
    return handler2;
  }

  private class Handler2 extends MouseAdapter {
    @Override public void mousePressed(MouseEvent e) {
      if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled()) {
        return;
      }
      boolean hasFocus = true;
      if (comboBox.isEditable()) {
        Component comp = comboBox.getEditor().getEditorComponent();
        if (!(comp instanceof JComponent) || ((JComponent) comp).isRequestFocusEnabled()) {
          hasFocus = comp.hasFocus() || comp.requestFocusInWindow();
        }
      } else if (comboBox.isRequestFocusEnabled()) {
        hasFocus = comboBox.hasFocus() || comboBox.requestFocusInWindow();
      }
      Component c = e.getComponent();
      if (!hasFocus && c instanceof AbstractButton) {
        ((AbstractButton) c).getModel().setPressed(false);
      }
      if (hasFocus) {
        togglePopup();
      }
    }

    @Override public void mouseReleased(MouseEvent e) {
      Component source = (Component) e.getSource();
      Dimension size = source.getSize();
      Rectangle bounds = new Rectangle(0, 0, size.width - 1, size.height - 1);
      if (!bounds.contains(e.getPoint())) {
        MouseEvent newEvent = SwingUtilities.convertMouseEvent(e.getComponent(), e, list);
        Point location = newEvent.getPoint();
        Rectangle r = new Rectangle();
        list.computeVisibleRect(r);
        if (r.contains(location)) {
          if (comboBox.getSelectedIndex() == list.getSelectedIndex()) {
            comboBox.getEditor().setItem(list.getSelectedValue());
          }
          comboBox.setSelectedIndex(list.getSelectedIndex());
        }
        comboBox.setPopupVisible(false);
      }
      hasEntered = false;
      stopAutoScrolling();
    }
  }
}
}}

* 解説 [#explanation]
- `InputVerifier`
-- `JTextField`に少なくとも`5`文字以上の入力が必要な`InputVerifier`を設定
-- `5`文字以下の場合は他のコンポーネントへのフォーカス移動が不可となる
- `Default`
-- `Default`の編集不可`JComboBox`は`InputVerifier`でフォーカス移動が拒否されていてもクリックするとドロップダウンリストが表示される
- `setFocusable(false)`
-- `setFocusable(false)`を設定した編集不可`JComboBox`はフォーカス移動前の`InputVerifier`による検証は実行されない
-- `setFocusable(false)`が設定されていても`JComboBox`をクリックしてドロップダウンリストを表示することは可能
- `setEditable(true)`
-- `Default`の編集可`JComboBox`は`InputVerifier`でフォーカス移動が拒否されても`ArrowButton`クリックするとドロップダウンリストが表示される
-- `ComboBoxEditor`がクリックされた場合はフォーカス移動が拒否されるとドロップダウンリスト表示もキャンセルされる
- `Override BasicComboPopup`
-- `BasicComboPopup`で`mousePressed`が実行されたとき`requestFocus()`の戻り値が無視されているので、この戻り値がフォーカス変更要求が確実に失敗する場合(`InputVerifier`の検証で拒否されるなど)の`false`なら`BasicComboPopup#togglePopup()`を実行しないよう修正
-- `ArrowButton`がクリックされた場合はその選択状態描画が残る場合がある?のでドロップダウンリストの表示がキャンセルされた場合は`JButton#getModel()#setPressed(false)`で選択状態をクリア

* 参考リンク [#reference]
- [https://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html How to Use the Focus Subsystem (The Java™ Tutorials > Creating a GUI With Swing > Using Other Swing Features)]
- [https://stackoverflow.com/questions/72240670/prevent-a-jcombobox-popup-from-appearing-passing-inputverifier java - Prevent a JComboBox popup from appearing. Passing InputVerifier - Stack Overflow]
-- `PopupMenuListener`を`JComboBox`に設定して`InputVerifier`でフォーカス移動が拒否されたらドロップダウンリスト表示をキャンセルする方法もある
- [[JPopupMenuを開く前に対象となるJTextFieldにFocusを移動する>Swing/FocusBeforePopup]]
- [[ComboBoxEditorにJLayerを設定し入力の妥当性を表示する>Swing/ComboBoxEditorVerifier]]
- [https://bugs.openjdk.org/browse/JDK-8285698 &#91;JDK-8285698&#93; Create a test to check the focus stealing of JPopupMenu from JComboBox - Java Bug System]

* コメント [#comment]
#comment
#comment