0

I'm trying to implement my own tooltip. I do this with a Timer and mouse listeners (for moved and exited). When the mouse is moved, the timer is reset, so the popup only shows when the mouse has been still. When the mouse exits the component, the popup is hidden. However, when the popup is shown at the cursor, the mouse is inside the popup, so mouseExited is called. The popup disappears, but, if the mouse remains still, it happens again, causing the popup to flicker. This can be prevented by moving the popup 1px over, so that the mouse isn't in the popup, but this doesn't solve the whole problem, because moving the mouse over the popup makes it disappear.

My MCVE:

private static Timer timer = new Timer(100, new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {
        JPanel pop = new JPanel(new GridLayout(0, 3));
        pop.setBackground(Color.BLUE);
        // Do calculations similar to what would actually be happening,
        // because otherwise the flicker is too fast to demonstrate on my screen
        for (double i = Math.random() * 12; i < 40; i++) {
            BufferedImage img = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB_PRE);
            Graphics2D g = img.createGraphics();
            g.drawString(Math.log(Math.sqrt(Math.random())) + "", 0, 32);
            g.dispose();

            pop.add(new JLabel(new ImageIcon(img)));
        }

        popup = PopupFactory.getSharedInstance().getPopup(panel, pop, x, y);
        popup.show();
    }
});
private static JPanel panel;
private static Popup popup;
private static int x, y;

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setSize(640, 480);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    MouseAdapter ma = new MouseAdapter() {

        @Override
        public void mouseMoved(MouseEvent e) {
            timer.setRepeats(false);
            if (popup == null) timer.restart();
            x = e.getXOnScreen(); // Adding one here eliminates the flicker problem,
            y = e.getYOnScreen(); // but still calls mouseExited entering the popup
        }

        @Override
        public void mouseExited(MouseEvent e) {
            if (popup != null) {
                popup.hide();
                popup = null;
            }
            timer.stop();
        }

    };

    panel = new JPanel();
    panel.setBackground(Color.GREEN);
    panel.addMouseListener(ma);
    panel.addMouseMotionListener(ma);
    frame.add(panel);

    frame.setVisible(true);
}

I was thinking that maybe the mouseExited method should check to see if the popup's mouseEntered was called, but I'm not sure how to do this, and it would also cause problems when the popup extends over the edges of the component. I would prefer that the mouse listener ignore the popup.

It would also be OK if the popup moved with the mouse, but I'm not sure how to do that either.

Community
  • 1
  • 1
ricky3350
  • 1,710
  • 3
  • 20
  • 35
  • Any reason why you don't wish to use [tool tips](https://docs.oracle.com/javase/tutorial/uiswing/components/tooltip.html)? – copeg Jul 28 '16 at 16:51
  • @copeg I have custom components that I want to put in them – ricky3350 Jul 28 '16 at 18:23
  • FWIW JToolTip extends JComponent - you can override `JComponent.createToolTip` to return your own customized JToolTip (should be noted this takes a bit of layout and size tweaking - you must set the Layout of JToolTip and override its `getPreferredSize` as mentioned [here](http://stackoverflow.com/questions/9522475/custom-java-tool-tip-with-swing-components-as-content-does-not-show-up) ). – copeg Jul 28 '16 at 18:55

2 Answers2

2

However, when the popup is shown at the cursor, the mouse is inside the popup, so mouseExited is called.

You can check that the MouseExited event coordinates occurred within the bounds of the JPanel, and only hide the Popup if the event occurred outside the JPanel bounds.

if ( panel.contains(e.getX(), e.getY() ) ){
    return;
}

Consider using tool tips - it does the event handling for you and is highly customizable...multi-line is achievable via html and you can change the color by changing the look and feel

Community
  • 1
  • 1
copeg
  • 8,290
  • 19
  • 28
0

Remove the close popup code in the mouse exited function (in main), changing it to:

@Override
public void mouseExited(MouseEvent e) {
    timer.stop();
}

Add a new MouseAdaptor to the popup menu, with a new mouseExited() function running similar code:

popup.addMouseMotionListener( new MouseAdaptor() {
    @Override
    public void mouseExited(MouseEvent e) {
         popup.hide();
    }
}

That way the popup menu is responsible for closing itself when the mouse leaves it.

Drgabble
  • 618
  • 5
  • 17