javaswinginvokelaterinputverifier

InputVerifier changes button background


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
 
public class ButtonBackground2 extends JFrame {

    public ButtonBackground2() {
        setSize(350, 200);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);

        JPanel p= new JPanel();
        JTextField tf = new JTextField();
        tf.setPreferredSize(new Dimension(100, 20));
        tf.setInputVerifier(new NonEmptyVerifier());
        p.add(tf);
        add(p, BorderLayout.CENTER);

        p= new JPanel();
        JButton btn = new JButton("Button");
        btn.setPreferredSize(new Dimension(80, 30));
//        btn.addMouseListener(new BtnBackgroundListener());
        p.add(btn);
        add(p, BorderLayout.SOUTH);
        setVisible(true);
    }
 
 
    public static void main(String arg[]) {
        EventQueue.invokeLater(ButtonBackground2::new);
    }
 
 
    class NonEmptyVerifier extends InputVerifier {
/*
        public boolean shouldYieldFocus(JComponent source, JComponent target) {
            return verify(source);
        }
*/
        public boolean verify(final JComponent input) {
            JTextField tf = (JTextField) input;
            if (tf.getText().trim().length()>0) {
                System.out.println("OK");
                return true;
            }
            JOptionPane.showMessageDialog(ButtonBackground2.this,
                        "Enter at least one character.",
                        "Missing input", JOptionPane.ERROR_MESSAGE);
            return false;
        }
    }


    class BtnBackgroundListener extends MouseAdapter {
        public void mousePressed(final MouseEvent e) {
            SwingUtilities.invokeLater(() -> {
                JButton btn= (JButton)e.getSource();
                if (!btn.hasFocus()) btn.getModel().setPressed(false);
            });
        }
    }
 
}



EDIT
Surprisingly I could reduce my actual code to a small portion to demonstrate the misbehaviour.

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

public class Y extends JFrame {
  public static final long serialVersionUID = 100L;

  public Y() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(300, 240);
    setLocationRelativeTo(null);

    add(createTextFieldPanel(), BorderLayout.CENTER);
    JButton bOK= new JButton("OK");
    bOK.addActionListener(e -> System.out.println("OK, input accepted."));
/*  Adding the following listener makes in case of erroneous input the focus
    locking of the textfield's InputVerifier shaky. The InputVerifier itself,
    however, works alright, as one sees from the unfailingly displayed error
    message.
*/
    bOK.addMouseListener(new BtnBackgroundListener());
    add(bOK, BorderLayout.SOUTH);
    setVisible(true);
  }


  static public void main(String args[]) {
    EventQueue.invokeLater(Y::new);
  }


  private JPanel createTextFieldPanel() {
    JPanel p= new JPanel(new FlowLayout(FlowLayout.LEFT));
    p.add(new JLabel("Input:"));
    MyTextField tf= new MyTextField(this);
    tf.setPreferredSize(new Dimension(95, 20));
    tf.setFont(new Font("Monospaced", Font.PLAIN, 13));
    p.add(tf);
    return p;
  }
}

-----------------------------------------------------------


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

public class MyTextField extends JTextField {
  public static final long serialVersionUID = 50161L;

  Component parent;

  public MyTextField(Component parent) {
    this.parent= parent;
    setInputVerifier(new InputVerifier() {
/*
      public boolean shouldYieldFocus(JComponent source, JComponent target) {
        return verify(source);
      }
*/
      public boolean verify(JComponent comp) {
        if (getText().equals("pass")) return true;
        JOptionPane.showMessageDialog(parent,
            "Input does not match the requested format.\n"+getText(),
            "Input error", JOptionPane.ERROR_MESSAGE);
        return false;
      }
    });
  }
}

So first we can say that Camickr was right in doubting that length/complexity of code was of any influence.
And second that in this demo, too, removing the MouseListener stops the focus from being released inappropriately.
So why is program ButtonBackground2 working and program Y only at times? Sometimes the incorrect input is accepted on the very first button click, sometimes one has to repeat the click several times.
By the way I'm running jdk 18, build 18+36-2087.


Solution

  • This suggestions builds on gthanop's suggestion and incorporates the OP's approach to reset the button model state when validation fails:

    import java.awt.GridLayout;
    import javax.swing.InputVerifier;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.SwingUtilities;
    
    public class Main5 {
    
        private static class MyInputVerifier extends InputVerifier {
    
            private boolean validInput = false;
            private JButton button;
    
            public MyInputVerifier (JButton button)
            {
                this.button = button;
            }
    
            @Override
            public boolean verify(final JComponent input)
            {
                validInput = ((JTextField) input).getText().equals("pass");
    
                if (!validInput)
                {
                    JOptionPane.showMessageDialog(input, "Verifier detected invalid input!");
    
                    button.getModel().setPressed(false);
                }
    
                return validInput;
            }
        }
    
        private static void createAndShowGUI() {
    
            final JTextField field1 = new JTextField(12),
                             field2 = new JTextField("Anything");
            final JButton accept = new JButton("Submit");
    
            final MyInputVerifier miv = new MyInputVerifier(accept);
            field1.setInputVerifier(miv);
    
            accept.addActionListener(e -> {
                if (miv.validInput)
                    System.out.println("Accepted!");
    //            else
    //                JOptionPane.showMessageDialog(accept, "Invalid input!");
            });
    
            final JPanel form = new JPanel(new GridLayout(0, 1));
            form.add(field1);
            form.add(field2);
            form.add(accept);
            
            final JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(form);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
        
        public static void main(final String[] args) {
            SwingUtilities.invokeLater(Main5::createAndShowGUI);
        }
    }