So I've made a basic UI that shows text fields that will alert the user if the contents is outside of given bounds by causing a label to appear below it. This occurs if the user hits return or Tabs away from the field.
There is also a button that will advance the program so long as all of the fields are correct. If they are not it will cause the text fields to 'flash' (animation background red back to white).
This works fine except for one little bug - if the user enters incorrect data and immediately clicks the button, without first leaving the field, the focus listener fires and adds the label below the text field, but the click button action never fires (no flash). This appears to be due to the FocusListener event calling revalidate() on the panel and or pack() on the enclosing jframe.
Here's a working minimal example to demonstrate the problem
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
/**
* Demonstration that running revalidate() or JFrame.pack() in FocusListener
* (on a textField) causes the action listener for a JButton to never fire.
*
* This is only a problem if the contents of the frame are changed (ie. only
* happens once when label is added).
*/
public final class App {
private App() {
JFrame jFrame = new JFrame();
JPanel main = new JPanel();
MyPanel myPanel = new MyPanel();
JButton button = new JButton("Click");
button.addActionListener(event->{
/* with the code as it is this will never print if
the button is pressed while textField has focus
and label is not yet displayed */
System.out.print("\n Click has been pressed");
});
main.add(myPanel);
main.add(button);
jFrame.add(main);
jFrame.pack();
jFrame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> new App());
}
/**
* Creates a panel containing a text box.
* when this text box looses focus it causes
* a label to be added and the frame to resize (.pack())
*/
class MyPanel extends JPanel{
JLabel label;
JTextField textField;
public MyPanel(){
label = new JLabel("hello");
textField = new JTextField(10);
this.add(textField);
textField.addFocusListener(new Handler(this));
}
private class Handler implements FocusListener{
JPanel panel;
public Handler(JPanel panel){
this.panel = panel;
}
@Override
public void focusGained(FocusEvent e) {
}
@Override
public void focusLost(FocusEvent e) {
// if this line is removed
panel.add(label);
// if the below two lines are removed
// button ActionListener will fire
panel.revalidate();
SwingUtilities.getWindowAncestor(panel).pack(); // side note if this is called is revalidate() needed?
}
}
}
}
Any advice on how to fix this would be great. One thing to note is that in the real program the MyPanel class would be in its own file, if that affects any solutions.
Cheers.
=============== EDIT ======================
I was running it in my IDE (vscode) where the problem occured. When I compiled it and ran it from the terminal it worked fine. I presume its some quirk of the IDE.