0

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;    
}
stalko
  • 1
  • 1
  • 1
    I think you can safely cut down on amount of code in this question without losing its meaning. For example, by using only single panel as example, and skipping all empty listener methods. This way it'd be much easier to see what happens, which means it'd be easier for us to help you. See also [Minimal, Complete and Verifiable example](https://stackoverflow.com/help/mcve) – M. Prokhorov Jul 04 '17 at 13:31
  • That aside, if you're looking for hint, then the only thing there is realistically, is event's `paramString`. Try inspecting that, maybe it'll help. – M. Prokhorov Jul 04 '17 at 13:38
  • @M.Prokhorov: have you tested paramString to see if it does help? Why this suggestion? – Hovercraft Full Of Eels Jul 04 '17 at 13:57
  • @HovercraftFullOfEels, no, I have not. This is just a shot in the darkness, and to be honest, I don't think whoever designed Swing was particularly interested in giving that sort of information in context of an event, so it may be not available at all, or available as part of some local context object deep inside framework itself. – M. Prokhorov Jul 04 '17 at 14:08
  • @M.Prokhorov: I can tell you that the `paramString` won't hold information to help the OP, but I agree with you that the library was not built to easily extract this information. I don't know if a decent kludge exists mainly because none of my programs have ever needed this feature. – Hovercraft Full Of Eels Jul 04 '17 at 14:10

2 Answers2

1

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.

camickr
  • 321,443
  • 19
  • 166
  • 288
1

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);