javamultithreadingswingfocusjtextcomponent

How to set my request focus event listener thread safe?


I m using the the focus event listener to implement a placeholder solution for all JTextComponents (JTextField, JTextArea ...) my implementation is as below

public class PlaceHolderDecorator {

public static void decorate(JTextField field, String placeHoldingText) {
    field.setForeground(Color.GRAY);
    field.addFocusListener(new FocusListener() {
        @Override
        public void focusGained(FocusEvent e) {
            if (field.getText().equals(placeHoldingText)) {
                field.setText("");
                field.setForeground(Color.BLACK);
            }
        }

        @Override
        public void focusLost(FocusEvent e) {
            if (field.getText().isEmpty()) {
                field.setForeground(Color.GRAY);
                field.setText(placeHoldingText);
            }
        }
    });
}
public static void decorate(JTextField field, String placeHoldingText,Color placeHolderColor,Color textColor) {
    field.setForeground(placeHolderColor);
    field.addFocusListener(new FocusListener() {
        @Override
        public void focusGained(FocusEvent e) {
            if (field.getText().equals(placeHoldingText)) {
                field.setText("");
                field.setForeground(textColor);
            }
        }

        @Override
        public void focusLost(FocusEvent e) {
            if (field.getText().isEmpty()) {
                field.setForeground(placeHolderColor);
                field.setText(placeHoldingText);
            }
        }
    });
}

final public static int DATE_FIELD_P = 10;
final public static int PHONE_FIELD_P = 14;

public static void decorate(JFormattedTextField field,String placeHoldingText,Color placeHolderColor,Color textColor,int checkFieldProperty){
    field.setForeground(placeHolderColor);
    final JFormattedTextField.AbstractFormatterFactory formatterFactory = field.getFormatterFactory();
    field.setFormatterFactory(null);
    field.setText(placeHoldingText);
    field.addFocusListener(new FocusListener() {
        @Override
        public void focusGained(FocusEvent e) {
            if (field.getText().equals(placeHoldingText)) {
                field.setText("");
                field.setForeground(textColor);
                field.setFormatterFactory(formatterFactory);
            }
        }

        @Override
        public void focusLost(FocusEvent e) {
            if (field.getText().trim().length()!=checkFieldProperty ) {
                field.setFormatterFactory(null);
                field.setText("");
            }
            if (field.getText().isEmpty()) {
                field.setFormatterFactory(null);
                field.setForeground(placeHolderColor);
                field.setText(placeHoldingText);
            }
        }
    });
 }
}

to use my place holder tool I do the following

    final Color writingColor = new Color(45, 45, 45);
    final Color holdingColor = new Color(127, 127, 127);

    PlaceHolderDecorator.decorate(jFTFDateDepot, "Date dépot du dossier", holdingColor, writingColor, PlaceHolderDecorator.DATE_FIELD_P);
    PlaceHolderDecorator.decorate(jtfNom, "Nom", holdingColor, writingColor);

this way when a focus occurs the place holder will be shown, after this I needed to creat a validation mechanisme for my inputs so I got the idea to check my field like this :

    public class RequiredFieldValidator {

        final private List<JTextComponent> components;
        private ErrorDialog dialog;

        public RequiredFieldValidator() {
            components = new ArrayList<>();
        }

        public void add(JTextComponent jTextComponent) {
            components.add(jTextComponent);
        }

        public boolean validate() {

            for (final JTextComponent component : components) {
                String placeHolder = component.getText();
                System.out.println("placeholder : " + placeHolder);

                component.requestFocus();
                if (component.getText().trim().isEmpty()) {
                    System.out.println("validation started");
                    dialog = new ErrorDialog(null, true);
                    dialog.showErrorDialog("Veuillez remplir le champs " + placeHolder + " obligatoir");
                    component.requestFocus();
                    return false;
                }

            }
            return true;
        }
    }

as you see in my code to get rid of the place holding text I do request a focus, where the text will be empty if the user haven't put any text if not the input of the user will remain

I do use this field validator like this

I register my required fields in the init section

    fieldValidator = new RequiredFieldValidator();

    fieldValidator.add(jFTFDateDepot);
    fieldValidator.add(jFTFDateNaissance);
    fieldValidator.add(jFTFNumTel);
    fieldValidator.add(jtfLieuNaissance);
    fieldValidator.add(jtfNom);
    fieldValidator.add(jtfPrenom);

then I fire the validation process on the create event.

My issue in this code is that before running I was expecting this LIKE sequence diagram on the create event

Getting current component >> requestFocus() >> fire place holding event >> do processing on the text of the component >> return to validation >> get the processed text >> if empty show alert >> if not loop to next component

but I was surprised to see that my request focus listener on the current component is firing later on the swing thread dispatcher

please how to make this thread safe, I need to fire the focus listener before the get text of the validation.


Solution

  • You should not need to request focus on a component in order to check if the component has text or not. The user will not appreciate it when the focus keeps moving to the last text field as you cycle through all the text fields to do the validation.

    So using your current approach, what you really need is a way to see if the text in the text field is the "place holder" value or not. So maybe you can combine the "place horder" logic with the validation logic so that both methods have access to the place holder value.

    Another approach is to not set the text of the text field to the place holder value. Then the validation logic can just check if the text field is empty or not. For a solution using this approach check out Text Prompt. It display a prompt without setting the text of the text field.