简体   繁体   中英

Mac L&F problems: Differing behavior of JTextField.requestFocus()

I have a problem with JTextField.requestFocus() behavior that appears to be different on Mac OS X.

Here is my situation: I have a dialog with a JList and a JTextField. The user is supposed to write a boolean expression in the text field, and the list contains the names of all the variables that might be entered in the expression. Because the user is expected to continue entering the expression after clicking on a variable from the list, the program helpfully calls JTextField.requestFocus(). This way you can eg click "pvalue" from the list and then type " < 0.05" without the need to click on the textfield in between.

This all works fine on my development machine (Linux), but I got a bug report from a Mac user that clicking on the list actually selects all text in the text field, making it easy to accidentally overwrite what was entered before.

I suspected this is a problem with the Mac look-and-feel, after some searching it seems that indeed there is a "Quaqua.TextComponent.autoSelect" property for the mac look-and-feel that seems to be related to this problem: http://www.randelshofer.ch/quaqua/guide/jtextcomponent.html

My general question is:

  • Can you suggest a workaround for this problem?

In case that is too broad, an answer to these subquestions would already be a big help:

  • A possible solution could be to change the property "Quaqua.TextComponent.autoSelect". How do I do that?
  • I'm not even sure what "Quaqua" is. It looks like it is a customized look and feel. What is the default look and feel for Mac OS X? Does it have a property similar to Quaqua.TextComponent.autoSelect?
  • Is there a possibility to tweak look and feel for a single component instance only? If so, how?
  • Is it possible to set the Mac look and feel on my Linux development machine so that I can actually confirm this bug (all the above is really based on hunches and suspicions)? If so, how?

Seems this is a bug of Mac OS. JTextFields select their contents when they gain focus though keyboard tab cycling. If the insertion point is in the middle of the text, the insertion point will remain and the entire text will not be selected.

As a workaround you can override this behavior with the following, it works fine for me:

textfield.setCaret(new DefaultCaret()).

More details you can refer to this and this .

To modify the default behaviour, you can set the system property to false before initializing the UI components: System.setProperty("Quaqua.TextComponent.autoSelect", "false"); To modify a single component, you can use JTextField#putClientProperty("Quaqua.TextComponent.autoSelect", Boolean.FALSE); .

You can find other MacOS L&F specific properties here:

Quaqua Look & Feel - User Guide

A workaround might be (and I haven't tested this) to make the JList that inserts the variable names unfocusable. That way the focus will remain in the text field when you click on an item in the list. I'd recommend to use setRequestEnabled(false) on the JList , so that they are still focusable if you tab to them, but clicking them with the mouse will not focus them.

I noticed when looking through the JavaDocs that requestFocus() "is discouraged because its behavior is platform dependent." You should use requestFocusInWindow() instead and see if the same problem occurs with it.

requestFocusInWindow is part of the Focus subsystem , introduced in Java 1.4.

On a side note, the default Apple Look and Feel has at least one property in the apple.laf namespace: apple.laf.useScreenMenuBar

Edit: According to Sun , the Macintosh look and feel is only available on Macs.

While using requestFocusInWindow() is indeed encouraged over requestFocus() , it still produces the same problematic behavior on Macs (eg, highlighting of full text field).

One workaround I got to work was to explicitly set the cursor position after requesting focus:

JTextField.requestFocusInWindow();
JTextField.setCaretPosition(JTextField.getDocument().getLength() - 1);

Note the "-1" is necessary, otherwise it will continue to highlight the entire field.

I'm curious to know if this solution is platform independent. Does this screw up the desired Linux or Windows behavior?

Sorry to add to an old question, but I just came across this problem and used the following code, which seems a little more complete than the previous example:

// JTextField linkedText
final int
  startBefore = linkedText.getSelectionStart(),
  endBefore = linkedText.getSelectionEnd();
linkedText.requestFocus(); // this was the original code line!
SwingUtilities.invokeLater(new Runnable()
{
  public void run()
  {
    linkedText.setSelectionStart(startBefore);
    linkedText.setSelectionEnd(endBefore);
  }
});

This appears to protect the current cursor position or selection. (Note: This code must already run in the event dispatch thread, but you need invokeLater anyway or it doesn't work.)

I have an 'is Mac' function, so I did this inside a test for that, but it probably doesn't do any harm to do it on all platforms.

Mac will select the contents of the text field when the field gains focus. You can restore the state of the text field if you listen for the focus change event.

// JTextField linkedText
// Cache the state of the JTextField prior to requesting focus
final int
  startBefore = linkedText.getSelectionStart(),
  endBefore = linkedText.getSelectionEnd();
linkedText.requestFocus(); // this was the original code line!

// Use a focus listener to listen for the focus change and then
// reset the selected text to protect the cursor position
linkedText.addFocusListener ( new FocusListener()
{
    public void focusGained( FocusEvent event ) {
        linkedText.setSelectionStart( startBefore );
        linkedText.setSelectionEnd( endBefore );
    }

    public void focusLost( FocusEvent event ) {
        // do nothing
    }
} );

Thank you for sharing your ideas. I had the same problem on my java application where on my windows system there wasn't a problem, but on my Mac OS X Yosemite I couldn't change the input. The focus wouldn't stay on the JTextField. Thanks to this thread I was able to fix my problem.

If you change the look and feel of the buttons and input boxes you maintain the focus and you can type again. The reset of the frame stays in the standard Mac OS look.

This is my code that I use in my java main methode. If you want to fix the problem past the try-catch code in your main methode.

public class Venster extends JFrame {

    public static void main(String[] args) {

        //Change L&F for mac
        //Mac JTextField Bug Fix
        try {
            // Set cross-platform Java L&F (also called "Metal")
            UIManager.setLookAndFeel(
                    UIManager.getCrossPlatformLookAndFeelClassName());
        } catch (UnsupportedLookAndFeelException e) {
            System.out.println("L&F not supported" + e.getMessage());

        } catch (ClassNotFoundException e) {
            System.out.println("Fout: " + e.getMessage());
        } catch (InstantiationException e) {
            System.out.println("Fout: " + e.getMessage());
        } catch (IllegalAccessException e) {
            System.out.println("Fout: " + e.getMessage());
        }

        //The app
        JFrame frame = new JFrame();
        frame.setSize(1000, 520);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setTitle("10 More Bullets by Frank Peters");
        frame.setContentPane(new SpeelVeld());
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);    //start app in center
    }
}

Soure: http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM