Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8215252

Allow to customize caret behavior in non-editable text components

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      Windows 10 Pro, 64 bit

      A DESCRIPTION OF THE PROBLEM :
      The current behavior in Swing is to hide the caret in a non-editable text
      component. A previous bug entry proposed to change this, i.e. to only hide the
      caret if the component is disabled:
      JDK-4248316 : DefaultCaret.focusGained does not reactivate caret if editable false
      (https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4248316).

      This was rejected with the reason that the current behavior is as intended.
      However, I am now proposing to make the behavior configurable, because we
      would like to change it in our project for the following reasons:

      1) An enabled component is focusable, but it's only through the caret and the
      selection that the user understands whether a text component is focused or not.
      When using the keyboard to navigate through multiple empty text components, it
      is unclear which component is focused until you hit an editable one. You can try
      this in the following example:

          public static void main(String[] args) {
              JFrame frame = new JFrame();
              Container c = frame.getContentPane();
              frame.setLayout(new BoxLayout(c, BoxLayout.PAGE_AXIS));
              c.add(readonly(new JTextField(20)));
              c.add(readonly(new JTextField(20)));
              c.add(new JTextField(20));
              c.add(readonly(new JTextField(20)));
              c.add(readonly(new JTextField("Select 'this' using keyboard!", 20)));
              frame.pack();
              frame.setVisible(true);
      frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
          }

          private static JTextField readonly(JTextField tf) {
              tf.setEditable(false);
              return tf;
          }

      2) It is easier to select text for copying it to the clipboard when a caret is
      shown. For example, you can move the caret in a readonly text field using the
      keyboard, but you don't see the caret's current position. To see this, try
      selecting the string "this" from the last text field in the example above,
      using the keyboard.

      3) Other UI platforms also show a caret in readonly fields.
      For example, readonly text fields in Windows 10 show a caret.
      Similarly, Firefox and Internet Explorer show a caret when focusing the
      following input field (whereas Chrome and Edge don't):
      <input type="text" value="non-editable" readonly/>

      Before posting this request, I was looking for an existing way to configure
      this behavior. In JDK-4248316, it was mentioned that one can customize the
      caret's behavior by plugging in a custom Caret implementation. I tried to do
      that, but without success.

      It seems that this should be done by creating a custom class that extends TextUI
      and overrides createCaret(), and setting that class as the UI-class of the
      corresponding component in the UIDefaults.
      However, this must be done for every subclass of JTextComponent that overrides
      JComponent.getUIClassID() (there are six in the JDK).
      Moreover, some Look & Feels have their own TextUI and Caret implementations.
      For example, in the Windows Look & Feel, there is the WindowsTextFieldUI class,
      whose createCaret() method returns a WindowsFieldCaret. The WindowsFieldCaret
      class overrides some protected methods, e.g. its getSelectionPainter() method
      returns a WindowsTextUI.WindowsPainter. So, to change the caret's focus behavior
      without changing anything else, one must subclass WindowsFieldCaret. But that's
      not possible because this class is package-private. It's also not possible to
      re-implement getSelectionPainter(), because WindowsTextUI.WindowPainter is
      package-private, too.

      Therefore, I propose to introduce some configuration mechanism, and to respect
      that configuration in the DefaultCaret class, like:

          public void focusGained(FocusEvent e) {
              if (component.isEnabled()) {
                  if (alwaysShow || component.isEditable()) {
                      setVisible(true);
                  }
                  setSelectionVisible(true);
              }
          }

      where alwaysShow would be a new field that stores the configured behavior.
          
      There are several possibilities for configuring the behavior, including the
      definition of a new system property, e.g.
      "javax.swing.plaf.showCaretWhenNotEditable", or some runtime configuration
      similar to the existing DefaultCaret.setUpdatePolicy().

      From the above options, I would prefer the system property because it is easier
      to configure globally, but any solution that allows global configuration is
      fine.

      If we agree on a solution, I can also implement it myself and submit a patch if
      you like.


            psadhukhan Prasanta Sadhukhan
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: