Question

I have a problem with the focus listener implemented by CustomTextField class. The focus listener is only called when another Swing component is getting the focus. But If I move the JFrame istelf by dragging it with the mouse, the focusLost() method is never called (in other words, it doesn´t seem that the focus is shifting from CustomTextField to JFrame).

EDIT: The solution of my question is below:

import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import javax.swing.*;

public class ScrollFocus extends JFrame {

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            new ScrollFocus();
        }
    });
}

public ScrollFocus() {
    this.setLayout(new BorderLayout());
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Vector<String> values = new Vector<>();
    values.add("a");
    values.add("b");
    values.add("c");
    values.add("d");
    values.add("e");
    JComboBox<String> comboBox = new JComboBox<>(values);
    JScrollPane scrollPane = new JScrollPane(comboBox);
    this.add(scrollPane, BorderLayout.NORTH);

    CustomTextField customTextField = new CustomTextField();
    this.add(customTextField, BorderLayout.CENTER);

    JButton button = new JButton("press");
    final JPopupMenu menu = new JPopupMenu("Menu");
    menu.add(new JMenuItem("Test"));
    button.setComponentPopupMenu(menu);
    this.add(button, BorderLayout.SOUTH);

    pack();
    setVisible(true);
}

class CustomTextField extends JTextField implements FocusListener {

    private CustomPopup customPopup = new CustomPopup();

    public CustomTextField() {
        this.addFocusListener(this);


        this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "VK_UP");
        this.getActionMap().put("VK_UP", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                setPopupSize();
                customPopup.show(CustomTextField.this, CustomTextField.this.getX(), CustomTextField.this.getY() + CustomTextField.this.getHeight());    
                customPopup.setSelectedIndex(0);
            }
        });
        this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "VK_DOWN");
        this.getActionMap().put("VK_DOWN", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                setPopupSize();
                customPopup.show(CustomTextField.this, CustomTextField.this.getX(), CustomTextField.this.getY() + CustomTextField.this.getHeight());
                customPopup.setSelectedIndex(0);
            }
        });
    }

    public void setPopupSize() {
        customPopup.setPopupSize(new Dimension(this.getWidth(), 110));
    }

    @Override
    public void focusGained(FocusEvent e) {
    }

    @Override
    public void focusLost(FocusEvent e) {
    }

    class CustomPopup extends JPopupMenu {
        String[] values = new String[]{"Value1", "Value2", "Value3", "Value4", "Value5", "Value6", "Value7",
                "Value8","Value9", "Value10", "Value11", "Value12", "Value13", "Value14", "Value15", "Value16",};
        JList<String> list = new JList<>(values);
        JScrollPane scrollPane = new JScrollPane(list);
        public int index = 0;

        public CustomPopup() {
            this.setLayout(new GridLayout(0,1));
            this.add(scrollPane);
            this.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    if(e.getKeyCode() == KeyEvent.VK_UP){
                        if(customPopup.index > 0)
                            customPopup.setSelectedIndex(--customPopup.index);
                    }
                    else if(e.getKeyCode() == KeyEvent.VK_DOWN){
                        if(customPopup.index < customPopup.getListSize()-1)
                            customPopup.setSelectedIndex(++customPopup.index);
                    }
                }
            });
            this.addFocusListener(new FocusAdapter() {
                @Override
                public void focusLost(FocusEvent e) {
                    index=0;
                }
            });
            pack();

        }

        public void setSelectedIndex(int index) {
            list.setSelectedIndex(index);
            list.ensureIndexIsVisible(index);
            requestFocus();
        }

        public int getListSize() {
            return values.length;
        }
    }
}
}
Was it helpful?

Solution

//customPopup.setVisible(true);
customPopup.show((JComponent)e.getSource(), 0, 20);

You should be using the show(...) method to show the popup. This must add some listeners to the popup so you will no longer need the FocusListener on the text field.

However, now this is a different problem. The text field loses focus so the Action never get invoked. That would be ok, but the JList never gains focus so it doesn't respond to the up/down keys unless you click on the list box first. I'm not sure what the problem is here.

Maybe you can try to make the popup, scrollpane and list all non-focusable so that focus remains on the text field?

OTHER TIPS

'Focus', which is admittedly a slightly ambiguous term, generally applies to a component, not to an entire window. We think of the "window with focus", but I think what we really mean is "the current window, the one which contains the focus." I would not expect focus_lost to be called if I moved the window (aka JFrame) itself.

Another way to think of it; if I had a text field, clicked in it, and typed a letter or two, I would see those letters in that text field. If I then moved the window slightly and typed another letter or two, I would still expect those letters to appear in that field. It still has focus, and never lost it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top