javatooltipflicker

Possible to stop flickering java tooltip in heavyweight mode?


Similar Q to ToolTip flicker in Java if outside JFrame?

A constantly updating lightweight tooltip works fine, but once it moves out the window bounds or is made heavyweight (by disabling lightweight popups), it's flicker city.

Tried the "-Dsun.awt.noerasebackground=true" hint which works inside a window, but at the expense of some painting artefacts over other components (this example is just a blank panel). Outside a window bounds it doesn't help, there's still horrible amounts of flicker.

Anyone know how to solve this? Or is it not currently possible?

Example is in this code -->

import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;


public class JTooltipFlickerTest extends JFrame {


JPanel panel;

static public void main (final String[] args) {
    new JTooltipFlickerTest ();
}

public JTooltipFlickerTest () {
    super ();
    //ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false); 
    //ToolTipManager.sharedInstance().setReshowDelay(0); 

    setTitle (this.getClass().toString());
    setSize (1024, 768);

    this.getContentPane().setLayout (new BorderLayout());

    SwingUtilities.invokeLater (
        new Runnable () {

            @Override
            public void run() {
                panel = new JPanel ();

                final MouseAdapter ma = new MouseAdapter () {

                    public void mouseMoved (final MouseEvent e) { 
                        panel.setToolTipText ("x: "+e.getX()+", y: "+e.getY());
                    }
                };  
                panel.addMouseMotionListener(ma);

                //panel.setDoubleBuffered(true);
                //panel.createToolTip().setDoubleBuffered(true);

                JTooltipFlickerTest.this.getContentPane().add (panel, "Center");                
                JTooltipFlickerTest.this.setVisible (true);
            }
        }
    );
}
}

Solution

  • Somehow, the javax.swing.RepaintManager class can help with the tool-tip repaint issue. The class that extends the RepaintManager below is taken from the example in Filthy Rich Client book (on Swing), Chapter 11, Repaint Manager: https://web.archive.org/web/20130105122825/www.curious-creature.org/2007/07/22/repaint-manager-demos-chapter-11/

    It is modified to repaint JTooltipFlickerTest's contentpane...

    Try to comment out installRepaintManager() call in the constructor and you will see the difference...

    import java.awt.BorderLayout;
    import java.awt.Container;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.RepaintManager;
    import javax.swing.SwingUtilities;
    
    public class JTooltipFlickerTest extends JFrame {
    
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        JPanel panel;
    
        static public void main (final String[] args) {
            new JTooltipFlickerTest ();
        }
    
    
        public JTooltipFlickerTest () {
            super ();
            panel = new JPanel ();          
    
            setTitle (this.getClass().toString());
            setSize (1024, 768);
    
            this.getContentPane().setLayout (new BorderLayout());
    
            SwingUtilities.invokeLater (
                    new Runnable () {
    
                        @Override
                        public void run() {
    
                            final MouseAdapter ma = new MouseAdapter () {
    
                                public void mouseMoved (final MouseEvent e) { 
                                    panel.setToolTipText ("x: "+e.getX()+", y: "+e.getY());
                                }
                            };  
                            panel.addMouseMotionListener(ma);
    
                            panel.setDoubleBuffered(true);
                            panel.createToolTip().setDoubleBuffered(true);
    
                            JTooltipFlickerTest.this.getContentPane().add (panel, "Center");                
                            JTooltipFlickerTest.this.setVisible (true);
                        }
                    }
            );
    
            this.setLocationRelativeTo(null);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            installRepaintManager();
    
        }
    
        private void installRepaintManager() {
            ReflectionRepaintManager manager = new ReflectionRepaintManager();
            RepaintManager.setCurrentManager(manager);
        }
    
        private class ReflectionRepaintManager extends RepaintManager {
            public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
    
                int lastDeltaX = c.getX();
                int lastDeltaY = c.getY();
    
                Container parent = c.getParent();
                while (parent instanceof JComponent) {
                    if (!parent.isVisible()) {
                        return;
                    }
    
                    if (parent instanceof JTooltipFlickerTest) {
                        x += lastDeltaX;
                        y += lastDeltaY;
    
                        int gap = getContentPane().getHeight() - h - y;
                        h += 2 * gap + h;
    
                        lastDeltaX = lastDeltaY = 0;
    
                        c = (JComponent) parent;
                    }
    
                    lastDeltaX += parent.getX();
                    lastDeltaY += parent.getY();
    
                    parent = parent.getParent();
                }
    
                super.addDirtyRegion(c, x, y, w, h);
            }
        }
    }
    

    edited

    When installRepaintManager() is disabled, the whole tooltip flickers on both sides of the the edge boundary (it is the same effect as in OP's original code).

    When installRepaintManager() is enabled, one part of the tooltip area doesn't flicker inside of the edge boundary. In contrast, the other part of it flickers outside of the edge boundary. But, the flicker is not so bad compared to when installRepaintManager() is disabled.

    I know, it is a subtle difference which I guess it leaves nothing to be desired of. At least, the words in the tooltip area are a little bit legible when installRepaintManager() is enabled.

    Even when the double-buffered codes are disabled, installRepaintManager() works as expected; that is, the heavyweight component is rapidly repainted to reduce the flicker.

    //panel.setDoubleBuffered(true);
    //panel.createToolTip().setDoubleBuffered(true);