Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JTextField get no focus when in a heavy-weight popup

Tags:

java

swing

popup

I want do display a text filed in a popup. When popup is completly over the application frame (MediumWeightPopup) - all works fine, but when a part of popup is outside of frame (HeavyWeightPopup) it cannot be focused. In this case caret is invisible and no text input is possible.

Here is my code:

import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class PopupTest {

    public static void main(String[] args) {
        JFrame frm = new JFrame("Popup test");
        JPanel p = new JPanel();
        p.addMouseListener(new MouseAdapter() {
            Popup pop;
            @Override
            public void mouseReleased(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    if (pop != null) {
                        pop.hide();
                    }
                    JPanel popupPanel = new JPanel(new BorderLayout());
                    JTextField field = new JTextField(20);
                    popupPanel.add(field);
                    pop = PopupFactory.getSharedInstance().getPopup(p, popupPanel, e.getXOnScreen(), e.getYOnScreen());
                    pop.show();
                    System.out.println("Popup type: " + pop.getClass().getName());
                    System.out.println("Can get focus? " + field.requestFocusInWindow());
                }
            }
        });
        frm.add(p);
        frm.setSize(500, 300);
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.setLocationRelativeTo(null);
        frm.setVisible(true);
    }
}

On right click near to right border of window I get a non-focusable text field. The same problem I get with any other component in popup that allows key control (for example JTable).

How can I focus a component in a HeavyWeightPopup?

like image 563
Sergiy Medvynskyy Avatar asked Feb 02 '18 13:02

Sergiy Medvynskyy


People also ask

What is the function of JTextField control?

A JTextField subclass that allows you to specify the legal set of characters that the user can enter. See How to Use Formatted Text Fields.

Which is super class of JTextField and JTextArea?

The JTextComponent is a superclass of JTextField that provides a common set of methods used by JTextfield. The important methods in the JTextField class are setText(), getText(), setEnabled(), etc.

How do I limit the length of a JTextField?

We can restrict the number of characters that the user can enter into a JTextField can be achieved by using a PlainDocument class.

What events does JTextField generate?

JTextField: generates "ActionEvent" using the ActionListener interface. When a user press "Enter", an ActionEvent is fired and performs tasks defined in actionPerformed() method. JComboBox: generates "ItemEvent" and "ActionEvent" implementing the ActionListener and ItemListener interface respectively.


2 Answers

I also struggled with this years ago. I can't figure out how to give initial focus to a component on the popup. Here are some of my questions/observations:

What is the Popup class used for?

I always thought that a Popup should have some basic functionality, such as the pupup should close when:

a) the escape key is pressed b) the popup loses focus

The popup class provides none of the above functionality and in fact seems to require some obscure code to even get the keyboard focus to work properly.

Using a JWindow seems to provide the same functionality as a Popup.

JPopupMenu seems to support both of the above requirements.

Run the following program:

a) click on each of the buttons b) click on an empty part of the frame

It appears to me that whenever you need a "popup" you should use a JPopupMenu.

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

public class PopupTest extends JFrame
{
    String[] numbers = { "one", "two", "three", "four", "five" };

    public PopupTest()
    {
        getContentPane().setLayout( new FlowLayout() );
        getContentPane().setBackground(Color.YELLOW);

        JButton popup = new JButton("Popup as Popup");
        popup.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                popupPopup(e);
            }
        });
        getContentPane().add(popup);
        JButton window = new JButton("Window as Popup");
        window.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                windowPopup(e);
            }
        });
        getContentPane().add(window);

        JButton menu = new JButton("PopupMenu as Popup");
        menu.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                menuPopup(e);
            }
        });
        getContentPane().add(menu);
    }

    private void popupPopup(ActionEvent e)
    {
        JList list = new JList(numbers);
        list.setSelectedIndex(0);

        PopupFactory factory = PopupFactory.getSharedInstance();
        Popup popup = factory.getPopup(this, list, getLocation().x, getLocation().y+100);
        //popup.show();

        Window window = SwingUtilities.windowForComponent(list);

        if (window != null)
        {
            window.setFocusableWindowState(true);
        }

        popup.show();
        KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(list);
    }

    private void windowPopup(ActionEvent e)
    {
        JList list = new JList(numbers);
        list.setSelectedIndex(0);

        JWindow window = new JWindow(this);
        window.getContentPane().add(list);
        window.pack();
        window.setVisible(true);
        window.setLocation(getLocation().x + 200, getLocation().y+100);

        window.addWindowListener( new WindowAdapter()
        {
            public void windowDeactivated(WindowEvent e)
            {
                System.out.println("deactivated");
            }
        });
    }

    private void menuPopup(ActionEvent e)
    {
        JList list = new JList(numbers);
        list.setSelectedIndex(0);

        JPopupMenu menu = new JPopupMenu();
        menu.add( new JTextField(10) );
        menu.add( list );

        menu.show((Component)e.getSource(), 0, 100);
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new PopupTest();
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setSize(500, 200);
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}

Edit:

Based on Sergiy's answer, this code was close to working. The difference in the popupPopup() method is that the show() method needs to be invoked AFTER the window is made focusable. Code updated to reflect this change.

like image 123
camickr Avatar answered Oct 19 '22 08:10

camickr


Source code analyse brought me another solution

pop = PopupFactory.getSharedInstance().getPopup(p, popupPanel, e.getXOnScreen(), e.getYOnScreen());
// some new stuff
Window win = SwingUtilities.windowForComponent(popupPanel);
if (win instanceof JWindow && win.getType() == Window.Type.POPUP) {
    win.setFocusableWindowState(true);
}
// continue old stuff
pop.show();

So the complete example looks like

import java.awt.BorderLayout;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JWindow;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class PopupTest {

    public static void main(String[] args) {
        JFrame frm = new JFrame("Popup test");
        JPanel p = new JPanel();
        p.addMouseListener(new MouseAdapter() {
            Popup pop;
            @Override
            public void mouseReleased(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    if (pop != null) {
                        pop.hide();
                    }
                    JPanel popupPanel = new JPanel(new BorderLayout());
                    JTextField field = new JTextField(20);
                    popupPanel.add(field);
                    pop = PopupFactory.getSharedInstance().getPopup(p, popupPanel, e.getXOnScreen(), e.getYOnScreen());
                    Window win = SwingUtilities.windowForComponent(popupPanel);
                    if (win instanceof JWindow && win.getType() == Window.Type.POPUP) {
                        win.setFocusableWindowState(true);
                    }
                    pop.show();
                    System.out.println("Popup type: " + pop.getClass().getName());
                    System.out.println("Can get focus? " + field.requestFocusInWindow());
                }
            }
        });
        frm.add(p);
        frm.setSize(500, 300);
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.setLocationRelativeTo(null);
        frm.setVisible(true);
    }
}

Interesting: the call field.requestFocusInWindow() still returns false, but field gets focus anyway.

BTW: this solution is also better for me because in my real code I get the Popup from a JComboBox (my goal is to create JTableComboBox with a table in popup and an optional filter field on top of the table).

like image 2
Sergiy Medvynskyy Avatar answered Oct 19 '22 08:10

Sergiy Medvynskyy