javaswingawtawt-eventqueue

in java awt or swing, how can I arrange for keyboard input to go wherever the mouse is?


Working on a help system, I'd like each component to offer some help when the the mouse is over it and the "?" key is pressed. Sort of like tooltips, except with much more extensive help - essentially a little web browser is intended to pop up and display text, images or more.

What I'm finding is that no matter where the mouse is, the input always goes to the same KeyListener. Is there only supposed to be one active at a time?

For what it's worth, this is the now-working version - thanks for suggestions!

    /**
     * Main class JavaHelp wants to support a help function so that when
     * the user types F1 above a component, it creates a popup explaining
     * the component.
     * The full version is intended to be a big brother to tooltips, invoking
     * an HTML display with clickable links, embedded images, and the like.
     */


    import javax.swing.*;
    import javax.swing.border.Border;
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;

    class Respond2Key extends AbstractAction
    {
    Component jrp;

    // Contract consructor
    public Respond2Key( String text)
    {
      super( text );
    }

    // Constructor that makes sure it gets done right
    public Respond2Key( String text, Component jrpIn)
    {
      super( text );
      System.out.println( "creating Respond2Key with component " + jrpIn
                                       .toString
                                        () );
      jrp = jrpIn;
    }

    public void setJrp( Component j) {
        jrp = j;
    }


    // Functionality: what is the response to a key
    public void actionPerformed(ActionEvent e)
    {
      // use MouseInfo to get position, convert to pane coords, lookup component
      Point sloc = MouseInfo.getPointerInfo().getLocation();

      SwingUtilities.convertPointFromScreen( sloc, (Component) jrp );

      Component c = jrp.getComponentAt( sloc );
      System.out.printf( "Mouse at %5.2f,%5.2f Component under mouse is %s\n",
                 sloc.getX(), sloc.getY(), c.toString() );
    }
    }


    //---------------------------------------------------------------- 
    // The main class
    //---------------------------------------------------------------- 
    public class JavaHelp extends JFrame
    {
    // The object constructor
    public JavaHelp()
    {
        // Start construction
        super( "Help System" );
        this.setSize( 640, 480 );
        Container contents = getContentPane();
        contents.setLayout( new FlowLayout() );


        JButton b1 = butt(  "button1", 64, 48 );
        JButton b2 = butt(  "button2", 96, 48 );
        JButton b3 = butt(  "button3", 128, 48 );
        JPanel p1 = pane( "hello", 100, 100 );
        JPanel p2 = pane( "world", 200, 100 );

        contents.add( b1 );
        contents.add( p1 );
        contents.add( b2 );
        contents.add( p2 );
        contents.add( b3 );

        JRootPane jrp = this.getRootPane();
        jrp.getInputMap( jrp.WHEN_IN_FOCUSED_WINDOW)
        .put( KeyStroke.getKeyStroke( "F1" ), "helpAction" );
        jrp.getActionMap().put( "helpAction",
                    new Respond2Key("frame",(Component)contents)
                    );
        this.setVisible( true );
        this.requestFocus();
        this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

    }

    // Inner classes for instantiating and listening to button, and panel.
    class ButtonListener implements ActionListener
    {
      private String label = null;

      public void setLabel(String s) {label = s;}

      public void actionPerformed(ActionEvent e)
      {
        System.out.printf( "Dealing with event labeled %s source %s\n\n",
                   label,
                   e.getSource().toString() );
      }

    }

    // def butt( from, name, w, h) = new Jbutton (...)
    protected JButton butt( String s, int w, int h)
    {
      JButton b = new JButton( s );
      b.setSize( w, h );
      ButtonListener oj = new ButtonListener();
      oj.setLabel( s );
      b.addActionListener( oj );
      return (b);
    }

    // def pane = new Jpanel(...)
    protected JPanel pane(String name, int w, int h)
    {
      JPanel p = new JPanel();
      p.setMinimumSize( new Dimension( w, h ) );
      p.add( new Label( name ) );
      p.setBackground( Color.black );
      p.setForeground( Color.red );
      return (p);
    }

    //--------------------------------
    public static void main(String[] args)
    {
      JavaHelp jh = new JavaHelp();
    }



    }






Solution

  • the input always goes to the same KeyListener.

    A KeyEvent is always dispatched to the component with focus, the mouse location has nothing to do with how the key event is generated.

    Instead of using a KeyListener, you should be using Key Bindings. When you using Key Bindings you can invoke an Action whenever a KeyStroke is generated by adding the binding to the root pane of the JFrame. Read the section from the Swing tutorial on Key Bindings for more information.

    Now in the Action that you create to listen for the "?" KeyStroke you can then:

    1. use the MouseInfo class to get the current mouse location.
    2. use the SwingUtilities.convertPointFromScreen(...) to convert the mouse point to be relative to the root pane
    3. then you can use the Conatiner.getComponentAt(...) to get the actual component the mouse is over
    4. once you know the component you can display your help information.