• 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

概要

JComboBoxへのマウスクリックによるフォーカス移動が別コンポーネントに設定されているInputVerifierの検証で拒否された場合、そのドロップダウンリストの表示をキャンセルします。

サンプルコード

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();
    }
  }
}
View in GitHub: Java, Kotlin

解説

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

参考リンク

コメント