Question

I have an application which uses JTextPane basically as a multi line label that can center text and wrap it automatically (centering is the reason I don't use a lighter component, but JTextArea would have the same issue). It is used as a part of a larger entity, which should use one tooltip for it. However, setting the tooltip for the containing component does not work for the JTextPane. Other components show the tooltip as intended.

SSCCE:

import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;

public class ToolTipTest {
    ToolTipTest() {
        JFrame frame = new JFrame("Tool tip test");
        frame.setLocationByPlatform(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel bg = new JPanel(new BorderLayout());
        frame.add(bg);
        bg.add(new JLabel("Tooltip shows here"), BorderLayout.NORTH);
        JTextPane text = new JTextPane();
        try {
            text.getStyledDocument().insertString(0, "Lorem ipsum dolor sit"
                    + " amet, consectetur adipiscielit, sed eiusmod tempor"
                    + " incidunt ut labore et dolore magna aliqua.", null);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
        text.setEditable(false);
        text.setFocusable(false);
        bg.add(text);
        bg.setToolTipText("A tooltip, that should show on both the label and the text");
        // Works, but is ugly and results in visible change in tooltip when
        // moving cursor between components
        // text.setToolTipText("A tooltip, that should show on both the label and the text");
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ToolTipTest();
            }
        });
    }
}

On the above sample the tooltip is displayed on the JLabel, but not on the JTextPane. I am currently setting the same tool tip text for the text pane as a workaround, but this results in a visible switch between tooltips when the user moves mouse cursor on top of the components.

Is there a way to show the tooltip of the parent component on JTextPane?

Edit: I tried overriding the getToolTipText() methods and registering the component with the tool tip manager, as suggested in the comments. This works, in the sense that the text pane then gets the tool tip text from the parent. However, the visual effect is exactly the same as in my workaround.

Was it helpful?

Solution 2

Somewhat based on idea by mKorbel, I made a glass pane style overlay atop the component group. In my case, all the components only provide information, and do not need user interaction, so the glass pane does not bother dispatching events to them.

I used OverlayLayout, as it was simplest and sufficient for my purposes. Applied to the code in the question, ToolTipTest constructor would be changed to:

ToolTipTest() {
    JFrame frame = new JFrame("Tool tip test");
    frame.setLocationByPlatform(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // Holds the component group and the glass pane
    JComponent content = new JPanel();
    content.setLayout(new OverlayLayout(content));
    frame.add(content);
    JComponent glassPane = new JComponent(){};
    content.add(glassPane);
    final JPanel bg = new JPanel(new BorderLayout());
    content.add(bg);
    bg.add(new JLabel("Tooltip shows here"), BorderLayout.NORTH);
    JTextPane text = new JTextPane();
    try {
        text.getStyledDocument().insertString(0, "The same tooltip also shows here", null);
    } catch (BadLocationException e) {
        e.printStackTrace();
    }
    text.setEditable(false);
    text.setFocusable(false);
    bg.add(text);
    glassPane.setToolTipText("A tooltip, that shows on both the label and the text");
    JLabel other = new JLabel("This component has another tooltip");
    other.setToolTipText("Another tooltip");
    frame.add(other, BorderLayout.SOUTH);

    frame.pack();
    frame.setVisible(true);
}

Edit: Included a label that is outside the component group with the shared tool tip, to differentiate from the more typical use of glass panes.

OTHER TIPS

Essentially, you need to proxy the tool tip functionality so that when requested, you pass back the value from the parent.

You also need to manually register the component with the ToolTipManager, otherwise it won't be asked for the tooltip...

Tooltip

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.BadLocationException;

public class ParentToolTip {

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

    public ParentToolTip() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JTextPane text = new JTextPane() {

                    @Override
                    public String getToolTipText() {
                        return ((JComponent) getParent()).getToolTipText();
                    }

                    @Override
                    public String getToolTipText(MouseEvent event) {
                        return ((JComponent) getParent()).getToolTipText(event);
                    }

                };
                try {
                    text.getStyledDocument().insertString(0, "Lorem ipsum dolor sit"
                            + " amet, consectetur adipiscielit, sed eiusmod tempor"
                            + " incidunt ut labore et dolore magna aliqua.", null);
                } catch (BadLocationException e) {
                    e.printStackTrace();
                }

                ToolTipManager.sharedInstance().registerComponent(text);

                JFrame frame = new JFrame("Testing");
                JPanel panel = new JPanel(new BorderLayout());
                panel.setToolTipText("This is not the tooltip your are looking for");
                frame.setContentPane(panel);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(text);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top