8 Replies Latest reply: Jan 24, 2013 1:57 AM by Jörg RSS

    Thread race

    Jörg
      Hello,

      the following code shows a textField with a focusListener and a button with an
      actionListener. I would like the focusListener not to perform its job whenever
      the button is pressed. Applying FocusEvent.getOppositeComponent() is of no avail,
      as the button receives focus in both cases, when tabbing out of the textField
      and when being clicked.
      I tried to set a flag in the actionListener and to check that flag in the
      focusListener. But even if I put focusLost in a SwingUtilities.invokeLater
      the focusEvent is fired before the ActionEvent. As a last resort I could set
      the button to not being focusable any more, but this entails other disadvantages.
      Any idea?
      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.*;
      
      public class Y extends JFrame {
        boolean buttonPressed;
      
        public Y() {
          setSize(300,300);
          setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
          setLayout(new FlowLayout());
          JTextField tf= new JTextField(10);
          tf.addFocusListener(new FocusAdapter() {
            public void focusLost(FocusEvent e) {
           SwingUtilities.invokeLater(new Runnable() {
             public void run() {
               if (buttonPressed) return;
               System.out.println("tf focus lost");
             }
           });
            }
          });
          add(tf);
          JButton b= new JButton("Action");
          b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
           buttonPressed= true;
           System.out.println("button action");
            }
          });
          add(b);
          pack();
          setVisible(true);
        }
      
      
        public static void main(String args[]) {
          EventQueue.invokeLater(new Runnable() {
            public void run() {
           new Y();
            }
          });
        }
      
      }
        • 1. Re: Thread race
          baftos
          You want after the click, the focus to go back to the text box?
          Maybe this?
              b.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
          
               tf.requestFocusInWindow();
                }
          • 2. Re: Thread race
            Jörg
            Hello Baftos, thanks for your reply
            You want after the click, the focus to go back to the text box?
            No, I want that when the textfield has got the focus and the button is clicked, the code in focusLost is not executed. When any other component in the original form is clicked (receives the focus) or when the textfield's focus is transferred to the button by means of the TAB-key, the code in focusLost should be executed.
            • 3. Re: Thread race
              EJP
              Why?
              • 4. Re: Thread race
                baftos
                You don't explain it well, IMHO. Please explain the behaviour you want (like a user, not a developer), not the code should or should not be executed. That code, as presented, does nothing anyway.
                • 5. Re: Thread race
                  Jörg
                  In focusLost(...) the input of the textfield is checked. If ok, further components of the form are filled according to the input; if erroneous, a message is displayed and the focus returns to the textField. An experienced user knows what to enter in the textField, but an unexperienced user will like to have some assistance. This assistance is provided when clicking the button. The then appearing dialog collects all necessary information to calculate the corresponding input to the textField.
                  Still too abstract?
                  Let's invent a simplified example. The textField asks for the average age of your family. If you count only few persons in your family, you could calculate the age mentally and provide the required input. But if you include all your ants, uncles and great-grandparents, the assisting dialog would present a possibility to enter each person's age and calculate the average for you.
                  In this example it is most likely that the textfield is empty when the button is pressed, so no problem when checking in focusLost(). But in my real application the assisting dialog can also be used for checking the input just made, so even erroneous input should be accepted at that time - which focusLost() does not. That's why I am looking for a way to return immediately from focusLost() before any checking is done, when the button is clicked.
                  A inputVerifier with the button set to setVerifyInputWhenFocusTarget(false) is no way either as the button receives focus also when tabbing out of the field.
                  Setting the button to setFocusable(false) is currently the only way I can see - with the drawback that the button gets accessible by mouse click only.
                  • 6. Re: Thread race
                    baftos
                    If I understand well, if the focus goes from the text field to the button, you want not to perform the calculations. If the focus goes from the text field to anything else, you want to perform the calculations.
                    I think FocusEvent.getOppositeComponent() would be good for this.
                    • 7. Re: Thread race
                      800268
                      Focus tends to not work well for that. I would suggest using a DocumentListener and do the validation whenever the value changes (and when pressing the button) (if expensive check, wait for input to be of a specific size or only after there are no changes for a while)

                      Show any errors or hint in an overlay icon next to the textfield or in a status bar, etc (for example, see Overlayable demo in Jide common).
                      • 8. Re: Thread race
                        Jörg
                        @Baftos
                        if the focus goes from the text field to the button, you want not to perform the calculations.
                        No, it's not the focus that counts, but the button click. If the user wants to have the button's action (dialog), then there should be no input checking in focusLost. FocusLost never does any "calculation". It checks the input and if valid fills other components with further information depending on the input.
                        If the focus goes from the text field to anything else, you want to perform the calculations.
                        Again, the "calaculations" are only performed when requested by the user through button click,
                        and this is the only situation when the checking in focusLost should be skipped.
                        I think FocusEvent.getOppositeComponent() would be good for this.
                        I wrote in my first post why this does not work.


                        @Walter
                        I see no way to determine with a DocumentListener when input is finished (Do you?). As in my application further information is displayed according to input, I have to wait until that moment.


                        @all
                        Thank you for your thoughts and suggestions. I came up now with a solution that is satisfying my requirements. I replaced the focusListener of the textfield by an inputVerifier, inserted a blank (thus invisible) JLabel between the textfield and the button, and attached a focusListener to that label. On focus gained the label immediately transfers the focus to the button. So this is the sequence which happens when tabbing out of the textField (inputVerifier is called).
                        The button is set to setVerifyInputWhenFocusTarget(false), so when clicking the button, input verification is not performed.
                        For today I live happily ever after.

                        Edited by: Jörg on 24.01.2013 08:52

                        P.S.:
                        By the way the introduction of the blank label allows also to keep the initial approach with the focusListener. As now tabbing out of the textField brings the focus to the label, we can make a difference between this case (1) and clicking the button (2) with FocusEvent.getOppositeComponent().