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.