6
votes

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();
    }



    }





3

3 Answers

3
votes

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.
3
votes

I'm sure there's a better way, but one quick and dirty solution:

private final class HoverFocusListener extends MouseInputAdapter {  
  public void mouseEntered(MouseEvent e) {
    e.getComponent().requestFocusInWindow();   
  }
}  

Or, if necessary:

public void mouseEntered(MouseEvent e) {
  e.getSource().setFocusable(true);
  for (Component c : refToParent.getComponents()) c.setFocusable(false);
  e.getComponent().requestFocusInWindow();   
 }

Then just .addMouseListener(new HoverFocusListener()) to all affected components.

0
votes

the input consistently goes to a similar KeyListener.

A KeyEvent is constantly dispatched to the part with the center, the mouse area steers clear of how the key occasion is created.

Rather than utilizing a KeyListener, you ought to utilize Key Bindings. At the point when you utilizing Key Bindings, you can conjure an Action at whatever point a KeyStroke is produced by adding the limiting to the root sheet of the JFrame. Peruse the part from the Swing instructional exercise on Key Bindings for more data.

Presently in the Action that you make to tune in for the "?" KeyStroke you can then, at that point:

utilize the MouseInfo class to get the current mouse area.

utilize the SwingUtilities.convertPointFromScreen(...) to change over the mouse highlight be comparative with the root sheet

then, at that point, you can utilize the Container.getComponentAt(...) to get the genuine segment the mouse is finished

when you know the part you can show your assistance data.