stalko
stalko

Reputation: 1

Higlight text in JTextField but only when tabbing

I want to create a JDialog where the text in the textfields is selected but only if the focus is gained from keyboard (TAB, CTRL+TAB). I have found several topics on this matter but had problems with implementing it.

Here is one which I was trying.

And my code:

public class Dialogg extends JDialog implements FocusListener, MouseListener {

private boolean focusFromMouse = false;

public Dialogg() {
    JTextField tf1 = new JTextField("text1");
    JTextField tf2 = new JTextField("text2");

    tf1.addMouseListener(this);
    tf2.addMouseListener(this);
    tf1.addFocusListener(this);
    tf2.addFocusListener(this);
}

@Override
public void focusGained(FocusEvent e) {
    if (!focusFromMouse) {
        JTextField tf = (JTextField) e.getComponent();
        tf.selectAll();
        focusFromMouse = true;
    }
}

@Override
public void focusLost(FocusEvent e) {
    focusFromMouse = false;
}

@Override
public void mouseClicked(MouseEvent e) {
    focusFromMouse = true;
}

}

It does not work as intended, it does not matter what is focus source the text always highlights. When I run the code and follow it step by step it turns out that focusGained code happens before mouseClicked code so the flag is not reset when it should. Any hints?

EDIT:

As suggested by M. Prokhorov I have deleted less relevant (for the question) lines from the code.Thank you.

EDIT 2:

I am trying to wrap focus listener as suggested by camickr. It looks like this now:

tf1.addFocusListener(new FocusAdapter() {
        public void focusGained(FocusEvent evt){
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                        if (!focusFromMouse){
                        tf1.selectAll();
                        focusFromMouse=true;
                    }                        
                }                
            });
        }
        public void focusLost(FocusEvent evt){
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    focusFromMouse=false;
                }
            });
        }
    });
public void mouseClicked(MouseEvent e) {
        focusFromMouse=true;        

I am printing line after each event to see the action order and still mouseClicked happens last. What am I doing wrong?

EDIT 3:

OK, I have found a solution which fulfils requirements of my simple Dialog. I could not find a way of doing this with use of invokeLater or EventQueue. Vladislav's method works but as I understand it restricts the user to only use the keyboard. I have used the initial approach but I have added an auxiliary variable and few conditions which allow to pass the flag "unharmed" trough Events that should not change the flag at given moment. It may not be subtle or universal but works for my app. Here is the code:

public void focusGained(FocusEvent e) {
    if(!focusFromMouse){
        if (higlight){
           JTextField tf = (JTextField) e.getComponent(); 
           tf.selectAll();
           focusFromMouse=false;    
        }
    }        
}

public void focusLost(FocusEvent e) {
    if (focusFromMouse){
        higlight=false;
        focusFromMouse=false; 
    }else{
        higlight=true;       
    }
}

public void mousePressed(MouseEvent e) {
    focusFromMouse=true;    
}

Upvotes: 0

Views: 61

Answers (2)

camickr
camickr

Reputation: 324147

When I run the code and follow it step by step it turns out that focusGained code happens before mouseClicked

Wrap the code in the FocusListener in a SwingUtilities.invokeLater(). The will place the code on the end of the Event Dispatch Thread (EDT), so the code will run after the variable in the MouseListener has been set.

See Concurrency in Swing for more information about the EDT.

Edit:

Just noticed the other answer. You might be able to do something simpler. Istead of listener for mouseClicked, listen for mousePressed. A mouseClicked event is only generated AFTER the mouseReleased event, so by that time the FocusListener logic has already been executed, even when added to the end of the EDT.

Edit 2:

If the above doesn't work then you might be able to use the EventQueue.peek() method to see if a MouseEvent is on the queue. This might even be easier than worrying about using the invokeLater.

Upvotes: 1

Vladislav Alexeev
Vladislav Alexeev

Reputation: 11

At the first, by default, focus on JTextField is requested by mouse-press event, not by mouse-click.

So, this method:

public void mouseClicked(MouseEvent e) {
    focusFromMouse = true;
}

is useless because the mouse-click event is triggered after the mouse-press event.

One way to solve your problem is to remove all native MouseListeners from JTextField:

...
for( MouseListener ml : tf1.getMouseListeners() ){
    tf1.removeMouseListener(ml);
}

for( MouseMotionListener mml : tf1.getMouseMotionListeners() ){
    tf1.removeMouseMotionListener(mml);
}
...

Another way is to handle all mouse events and consume those of them, which are triggered by JTextField:

Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
  @Override
  public void eventDispatched(AWTEvent event) {
    if( event.getSource() == tf1 ){
      ((MouseEvent)event).consume();
    }
  }
}, AWTEvent.MOUSE_EVENT_MASK);

Upvotes: 1

Related Questions