3

the normal behaviour of native text fields in many environments is as follows:

Textfield with text "abcdefg". I use the mouse to select "efg" from left to right. The caret is now behind "g". When I move the caret to the left by pressing the cursor left key once, the selection is removed and the caret is right before "e". When I do the same in a JTextField or JTextArea (tested on Mac OS) doing the exact same thing results in the caret being right before "g".

I know how I could change that programmatically by using a KeyListener and registering it with each component but I am looking for a way to change that for my entire application. Is that possible? Is there a Flag, I am not finding or do I have to hack my look and feel?

Thanks

mKorbel
  • 109,525
  • 20
  • 134
  • 319
user1573546
  • 523
  • 5
  • 13
  • Please also tell us why would you like to do this? – joey rohan Mar 21 '13 at 17:58
  • Why aren't you using the backspace key to erase the selected text? – Gilbert Le Blanc Mar 21 '13 at 17:59
  • I am just trying to have the same behavior as users know from text fields in browsers and other native applications. no other reason. In this use case it is not about deleting text but moving around in it, maybe after copying a word. I have noticed that I, myself am surprised by this behavior of our very own application when I use it and then checked why I found that surprising by comparing to other applications and found out they behave differently and that was what "broke my editing flow". However, I did not mean to start a diskussion on how to edit text ;-). – user1573546 Mar 21 '13 at 18:46

2 Answers2

2

I am looking for a way to change that for my entire application

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;

public class CaretAction extends TextAction
{
    private boolean caretForward;

    public CaretAction(boolean caretForward)
    {
        super(caretForward ? "Caret Forward" : "Caret Backward");
        this.caretForward = caretForward;
    }

    public void actionPerformed(ActionEvent e)
    {
        JTextComponent textComponent = getFocusedComponent();

        int start = textComponent.getSelectionStart();
        int end = textComponent.getSelectionEnd();
        int offset = textComponent.getCaretPosition();

        if (start != end)
        {
            if (caretForward)
                offset = (offset == end) ? offset + 1 : end;
            else
                offset = (offset == start) ? offset -1 : start;
        }
        else
        {
            offset += (caretForward) ? 1 : -1;
        }

        offset = Math.max(offset, 0);
        offset = Math.min(offset, textComponent.getDocument().getLength());
        textComponent.setCaretPosition( offset );
    }

    private static void createAndShowUI()
    {
        JTextField textField1 = new JTextField(10);
        JTextField textField2 = new JTextField(10);

        JPanel panel = new JPanel();
        panel.add( textField1 );
        panel.add( textField2 );

        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( panel );
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );

        ActionMap map = (ActionMap)UIManager.get("TextField.actionMap");
        map.put(DefaultEditorKit.backwardAction, new CaretAction(false));
        map.put(DefaultEditorKit.forwardAction, new CaretAction(true));
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}

You would also need to change the ActionMap for JTextArea, JFormattedTextField ...

camickr
  • 321,443
  • 19
  • 166
  • 288
  • Great, thanks! The only thing that I need to find out now, is where to put the registration code. ActionMap map = (ActionMap)UIManager.get("TextField.actionMap"); It works reliably after instantiating the first textfield but I need to do it somewhere central before that happens (or live with the workaround of calling new JTextField() just to ensure the action map property is already there). So far I have not been able to find reliable information when this property is set (other than after the first use of a JTextField. – user1573546 Mar 22 '13 at 14:30
  • I was actually going to post a question about that issue. When I was reading up on `UIManger.get()` (or was it UIDefaults.get()) it seemed to imply that the first call to "get" would create the object if it was using lazy creation. But it didn't seem to work for me either. So my solution was just to create a dummy text field as well. – camickr Mar 22 '13 at 15:48
  • Yes, I googled to find information about the UIManager lifecycle to find an answer to this but didn't come up with any valuable information. Will you post the question? If not I probably will. Thanks – user1573546 Mar 22 '13 at 22:49
  • I'll let you post the question since I have no immediate need for an answer, just curiosity. – camickr Mar 23 '13 at 00:20
  • OK, here it is: http://stackoverflow.com/questions/15584605/how-to-globally-change-the-actionmap-of-swing-jtextfields – user1573546 Mar 23 '13 at 07:55
1

What you can do is addCaretListener :

     anyField.addCaretListener(new CaretListener() {
        public void caretUpdate(CaretEvent evt) {
            anyFieldCaretUpdate(evt);
        }
       });

And set the Caret at the last again:

  private void anyFieldCaretUpdate(CaretEvent evt) {

     anyField.setCaretPosition(anyField.getText().length());
        }
joey rohan
  • 3,505
  • 5
  • 33
  • 70
  • 1
    I just found a very similar question: http://stackoverflow.com/questions/5951428/setting-caret-position-in-jtextarea this is the behavior I am after but my question is really if there is a way to enable this centrally. – user1573546 Mar 21 '13 at 18:57
  • @user1573546 suppose you don't use a key press, but use a mouse to highlight from back to end, in that case, this eg will also work, but not the link one :) – joey rohan Mar 22 '13 at 12:33
  • @user1573546 Also, in that case, the above answer by camickr is great! – joey rohan Mar 22 '13 at 12:35