javaswingeventslistenerfocuslistener

Is there anything that can prevent a FocusListener from working correctly?


I'm trying to add a FocusListener over some JTextFields and JFormattedTextFields , did all right, and when the class loads up, the listener works properly, it's a simple method to select all text on the TextFields when focus is gained, but the TextField.selectAll() only works once, then when I focus something else, ant return the focus to the Field it does nothing, but the listener itself is working, I tried adding a System.out.println(evt.getComponent()) and it prints out the result, but the focus does not select all text anymore...

The whole Frame class is huge and very specific with some IDE generated code ¯\_(ツ)_/¯, thats why I'm adding just two fields, that I believe may help understand what I've got here...

    tfVencimento = new javax.swing.JFormattedTextField(dateMask());
    tfValorBruto = new javax.swing.JFormattedTextField(decimalMask());
    tfValorBruto.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(new java.text.DecimalFormat("#,##0.00"))));

    tfValorBruto.setText("0,00");
    tfValorBruto.setMargin(new java.awt.Insets(2, 4, 2, 4));
    tfValorBruto.setMinimumSize(new java.awt.Dimension(70, 22));
    tfValorBruto.setPreferredSize(new java.awt.Dimension(70, 22));

    tfValorBruto.addFocusListener(new java.awt.event.FocusAdapter() {
        public void focusGained(java.awt.event.FocusEvent evt) {
            FocusGained(evt);
        }
    });
    tfVencimento.setMargin(new java.awt.Insets(2, 4, 2, 4));
    tfVencimento.setMinimumSize(new java.awt.Dimension(70, 22));
    tfVencimento.setPreferredSize(new java.awt.Dimension(70, 22));
    tfVencimento.addFocusListener(new java.awt.event.FocusAdapter() {
        public void focusGained(java.awt.event.FocusEvent evt) {
            FocusGained(evt);
        }
    });
    private void FocusGained(java.awt.event.FocusEvent evt) {                             
        JTextField field = (JFormattedTextField) evt.getComponent();
        field.selectAll();
    }

I thought it could be something that overrides my listener, didn't find anything...
Maybe the way I applied the listener, tried a few ways and all got there...

If I apply on each Field a listener, with a different method on each (like code below), it works, but I have 10+ Fields, so it's boring to do, and a hard maintaining code...

    tfVencimento.addFocusListener(new java.awt.event.FocusAdapter() {
        public void focusGained(java.awt.event.FocusEvent evt) {
            tfVencimentoFocusGained(evt);
        }
    });

    private void tfVencimentoFocusGained(java.awt.event.FocusEvent evt) {                             
        tfVencimento.selectAll();
    }

Solution

  • Solution

    I see a couple of options:

    Option A. Call tfValorBruto.setCaret(new DefaultCaret());

    Option B. In FocusGained: move the call to selectAll() inside an invoke-later Runnable, so it runs a little bit later. Like this:

        private void FocusGained(java.awt.event.FocusEvent evt) {
            JTextField field = (JFormattedTextField) evt.getComponent();
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    field.selectAll();
                }
            });
        }
    

    The Problem

    I used this code to help debug it:

            tfValorBruto = new javax.swing.JFormattedTextField() {
    
                public void moveCaretPosition(int pos) {
                    super.moveCaretPosition(pos);
                }
    
                public void setCaretPosition(int position) {
                    super.setCaretPosition(position);
                }
            };
    

    You can add a debugger breakpoint in moveCaretPosition(int). It showed that on FOCUS_GAINED events someone was calling moveCaretPosition(0)

    Breakpoint reached
        at com.test.FocusTest$1.moveCaretPosition(FocusTest.java:22)
        at com.apple.laf.AquaCaret.focusGained(AquaCaret.java:135)
        at java.awt.AWTEventMulticaster.focusGained(AWTEventMulticaster.java:228)
        at java.awt.AWTEventMulticaster.focusGained(AWTEventMulticaster.java:227)
        at java.awt.AWTEventMulticaster.focusGained(AWTEventMulticaster.java:227)
        at java.awt.Component.processFocusEvent(Component.java:6509)
        at javax.swing.JFormattedTextField.processFocusEvent(JFormattedTextField.java:645)
        at java.awt.Component.processEvent(Component.java:6376)
        at java.awt.Container.processEvent(Container.java:2266)
        at java.awt.Component.dispatchEventImpl(Component.java:4995)
        at java.awt.Container.dispatchEventImpl(Container.java:2324)
        at java.awt.Component.dispatchEvent(Component.java:4827)
        at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1952)
        at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:1061)
        at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:687)
        at java.awt.Component.dispatchEventImpl(Component.java:4876)
        at java.awt.Container.dispatchEventImpl(Container.java:2324)
        at java.awt.Component.dispatchEvent(Component.java:4827)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
        at java.awt.EventQueue$4.run(EventQueue.java:720)
        at java.awt.EventQueue$4.run(EventQueue.java:714)
        at java.security.AccessController.executePrivileged(AccessController.java:776)
        at java.security.AccessController.doPrivileged(AccessController.java:399)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
        at java.awt.EventQueue$5.run(EventQueue.java:747)
        at java.awt.EventQueue$5.run(EventQueue.java:745)
        at java.security.AccessController.executePrivileged(AccessController.java:776)
        at java.security.AccessController.doPrivileged(AccessController.java:399)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
    

    So in my case: the default look-and-feel implementation (Aqua) was trying to set up some default behavior when it received a FOCUS_GAINED event.

    We can either replace the Caret (that's solution A), or we can change the selection again after Aqua (that's solution B)