Summary

JTextFieldへのカーソルキー入力で水平スクロールが発生する場合のスクロールスパンを変更します。

Source Code Examples

// @see WindowsTextFieldUI.WindowsFieldCaret
class HorizontalScrollCaret extends DefaultCaret {
  @Override protected void adjustVisibility(Rectangle r) {
    EventQueue.invokeLater(() -> {
      JTextComponent c = getComponent();
      if (c instanceof JTextField) {
        horizontalScroll((JTextField) c, r);
      }
    });
  }

  private void horizontalScroll(JTextField field, Rectangle r) {
    TextUI ui = field.getUI();
    int dot = getDot();
    Position.Bias bias = Position.Bias.Forward;
    Rectangle startRect = null;
    try {
      startRect = ui.modelToView(field, dot, bias);
    } catch (BadLocationException ble) {
      UIManager.getLookAndFeel().provideErrorFeedback(field);
    }
    Insets i = field.getInsets();
    BoundedRangeModel vis = field.getHorizontalVisibility();
    int x = r.x + vis.getValue() - i.left;
    int n = 8;
    int span = vis.getExtent() / n;
    if (r.x < i.left) {
      vis.setValue(x - span);
    } else if (r.x + r.width > i.left + vis.getExtent()) {
      vis.setValue(x - (n - 1) * span);
    }
    if (startRect != null) {
      try {
        Rectangle endRect = ui.modelToView(field, dot, bias);
        if (endRect != null && !endRect.equals(startRect)) {
          damage(endRect);
        }
      } catch (BadLocationException ble) {
        UIManager.getLookAndFeel().provideErrorFeedback(field);
      }
    }
  }
}
View in GitHub: Java, Kotlin

Explanation

  • LookAndFeel Default
    • デフォルトのJTextFieldでは現在のLookAndFeelが使用するCaretに依存してカーソルキー入力による水平スクロールスパン(移動量)が決まる
    • MetalLookAndFeelNimbusLookAndFeelの場合、DefaultCaretJTextComponent#scrollRectToVisible(...)を使用して1文字分ずつ滑らかにスクロールする
    • WindowsTextFieldUIの場合、WindowsTextFieldUI.WindowsFieldCaretが同様にJTextComponent#scrollRectToVisible(...)を使用してスクロールするが、その移動量はJTextFieldに設定されたBoundedRangeModelのエクステント(JTextFieldの表示可能なサイズ)の1/4(int quarterSpan = vis.getExtent() / 4;)ずつのスパンでジャンプ・スクロールになる
  • DefaultCaret
    • WindowsLookAndFeelでも1文字分ずつスクロールするようJTextFieldDefaultCaretを設定
  • override DefaultCaret#adjustVisibility(...)

Reference

Comment