2

I am playing around with the KeyboardFocusManager and my own custom KeyEventDispatcher that redispathes all KeyEvents to one particular JTextField, regardless of focus within the JFrame. This works like a charm as far as textual input is concerned but I also want the JTextField to post its text to a JTextArea when a KeyEvent.VK_ENTER is redispatched to it. For some reason it just won't do this. I've set an actionListener on the JTextField which will be fired on if I have the cursor in the text field and press ENTER, however it's not fired on if the ENTER event comes from a KeyboardFocusManager.redispatchEvent(keyEvent).

I've also tried redispathing an ActionEvent rather than the unchanged KeyEvent in the case ENTER is pressed, but to no avail :( You would think that dispathing an ActionEvent to a component would fire it's ActionListeners but nope.

Can someone explain why this is? And maybe propose a neat way around it?

SSCCE:

package viewlayer.guiutil.focus;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.Runnable;import java.lang.String;
import java.util.Date;

public class Test extends JFrame
{
    private JTextField m_chatInput;
    private JTextArea m_textArea;

    public static void main(String... args)
    {
        Test test1 = new Test();
        test1.run(test1);
    }

    public void run(final Test test)
    {
        test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        test.setSize(250, 400);

        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new MyKeyEventDispatcher());

        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                test.init();
            }
        });

        test.setVisible(true);
    }

    public void init()
    {
        JPanel panel = new JPanel (new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(2,5,1,1);
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.gridwidth = GridBagConstraints.REMAINDER;

        JLabel chatLabel = new JLabel("Chat input field:");
        panel.add(chatLabel,gbc);

        m_chatInput = new JTextField(15);
        m_chatInput.setActionCommand(MyActionListener.ACTION_PERFORMED);
        m_chatInput.addActionListener(new MyActionListener());
        panel.add(m_chatInput,gbc);

        JTextField chatInput = new JTextField(15);
        panel.add(chatInput,gbc);

        JLabel text = new JLabel("chat history:");
        panel.add(text,gbc);

        m_textArea = new JTextArea(5, 15);
        m_textArea.setFocusable(false);
        panel.add(m_textArea,gbc);

        JButton postButton = new JButton("Post");
        postButton.setActionCommand(MyActionListener.ACTION_PERFORMED);
        postButton.addActionListener(new MyActionListener());
        panel.add(postButton,gbc);
        gbc.weighty = 1.0;
        gbc.anchor = gbc.NORTHWEST;

        setLayout(new FlowLayout(FlowLayout.LEFT));
        add(panel);
    }

    private class MyKeyEventDispatcher implements KeyEventDispatcher
    {
        public boolean dispatchKeyEvent(KeyEvent keyEvent)
        {
            KeyboardFocusManager.getCurrentKeyboardFocusManager().redispatchEvent(m_chatInput, keyEvent);

            return false;
        }
    }

    private class MyActionListener implements ActionListener
    {
        private static final String ACTION_PERFORMED = "ACTION_PERFORMED";

        public void actionPerformed(ActionEvent actionEvent)
        {
            if(actionEvent.getActionCommand().equals(ACTION_PERFORMED))
            {
                Date date = new Date(System.currentTimeMillis());
                m_textArea.append(date.getHours() +":"+ date.getMinutes() +":"+ date.getSeconds() + " - " + m_chatInput.getText() + "\n");
                m_chatInput.setText("");
            }
        }
    }
}
Oskar Lund
  • 339
  • 2
  • 11

3 Answers3

3

The text field code has a test to make sure the component has focus before invoking the listener code:

    public void actionPerformed(ActionEvent e) {
        JTextComponent target = getFocusedComponent();
        if (target instanceof JTextField) {
            JTextField field = (JTextField) target;
            field.postActionEvent();
        }
    }

However, you should be able to invoke the postActionEvent() method directly from your KeyEventDispatcher:

if (enter key)
   m_chatInput.postActonEvent();
else
   // redispatch the event
camickr
  • 321,443
  • 19
  • 166
  • 288
  • I take it the answer to my original question is YOU CAN'T (unless maybe if you extend JTextField or something). Your solution works for the SSCCE so I give you the accept. Thank you! – Oskar Lund Feb 24 '11 at 15:28
0

You should remove this line:

m_chatInput.setFocusable(false);

And above the line:

Date date = new Date(System.currentTimeMillis());

you should insert:

requestFocusInWindow(m_chatInput);

Please post if this worked for you or if we have to tweak a little more.

Bernd Elkemann
  • 23,242
  • 4
  • 37
  • 66
  • setFocusable(false) was just to further emphesize what I want to do, it doesn't really matter if it's focusable or not since I don't want to set the focus back to my m_chatInput. The actual problem I want to solve is to keep directing input to the chat even if it loses focus as a result of some other event in the application. This is to avoid having to manually set focus back to the chat (by mouse click) to be able to finish writing and sending the text. Maybe should have put this in the original question :) – Oskar Lund Feb 22 '11 at 14:00
  • Also, as of now, I won't even reach the actionPerformed method unless focus is already on m_chatInput. – Oskar Lund Feb 22 '11 at 14:03
  • Yea i understood that. You are trying {if not in focus -> redirect, if in focus -> redirect} and i am trying {if not in focus -> redirect, if in focus -> happyStayThatWay} – Bernd Elkemann Feb 22 '11 at 14:04
  • Yep in the case that m_chatInput already has the focus your custom KeyboardFocusManager will not get triggered so just install another action-listener in m_chatInput – Bernd Elkemann Feb 22 '11 at 14:05
  • I've updated the SSCCE with another JTexField and made both this and m_chatInput focusable. Now you can see that text is always added to m_chatInput regardless of which text field has focus/cursor. However, sending by hitting ENTER key will only work if m_chatInput has the focus... weird. – Oskar Lund Feb 22 '11 at 14:13
  • Nope, the redispatch is always triggered actually, regardless of where focus is. It's the MyActionListener.actionPerformed (the listener on m_chatInput) that will only get called if focus is in m_chatInput – Oskar Lund Feb 22 '11 at 14:21
  • Also tried redispathing an ActionEvent rather than just the KeyEvent in the case ENTER is pressed, but to no avail :( – Oskar Lund Feb 22 '11 at 14:23
0

I think i found a work-around you can use to keep the focus on the chatInput:

requestFocusInWindow(m_chatInput);
KeyboardFocusManager focusManager =
KeyboardFocusManager.getCurrentKeyboardFocusManager();
focusManager.addPropertyChangeListener(
    new PropertyChangeListener() {
        public void propertyChange(PropertyChangeEvent e) {
            String prop = e.getPropertyName();
            if ("focusOwner".equals(prop)) {
            requestFocusInWindow(m_chatInput);
            }
        }
    }
 );

I modified this based on some code found at:

http://download.oracle.com/javase/tutorial/uiswing/misc/focus.html

Bernd Elkemann
  • 23,242
  • 4
  • 37
  • 66
  • Thanks for your effort eznme. However it's not a question of focus, actually it's even a requirement that I don't let the chat retain or steal the focus back. I see how my earlier comment "This is to avoid having to manually set focus back to the chat (by mouse click) to be able to finish writing and sending the text" could confuse you, it's the second part of this that is important, to let the user finish what he was writing and send it using ENTER even if focus is lost on the chat. – Oskar Lund Feb 22 '11 at 15:05