Text-mouseover popups over a Swing JTextArea?
Is there anything out there that allows you to show a small text popup window (like a tooltip) over individual words or letters in a Swing JTextArea? (Or a JTextArea alternative with similar functionality.)
What I need should behave like a tooltip, in other words only display the popup text after the mouse has hovered over the word for a second or two, and it would vanish automatically once the mouse moves away. Of course the tricky part here is that I want it at the character/word level within the text, not at 开发者_StackOverflow中文版the component level... any suggestions?
You can override getToolTipText(Mouse Event event)
as needed.
Addendum: JTextComponent
, the parent of JTextArea
provides location information via two methods: modelToView()
and viewToModel()
. The latter should let you translate the mouse location into a document offset.
Of course the tricky part here is that I want it at the character/word level within the text
You use the mouse point to determine where you are in the text area:
int offset = textArea.viewToModel(...);
Now that you have an offset you can get the character or word at that location. The Utilities class has methods like getWordStart() and getWordEnd().
Then you use the getText(...) method to get the word or the character.
maybe
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
import java.util.*;
import javax.swing.event.*;
public class SimplePaintSurface implements Runnable, ActionListener {
private static final int WIDTH = 1250;
private static final int HEIGHT = 800;
private Random random = new Random();
private JFrame frame = new JFrame("SimplePaintSurface");
private JPanel tableaux;
@Override
public void run() {
tableaux = new JPanel(null);
for (int i = 1500; --i >= 0;) {
addRandom();
}
frame.add(tableaux, BorderLayout.CENTER);
JButton add = new JButton("Add");
add.addActionListener(this);
frame.add(add, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH, HEIGHT);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
tableaux.requestFocusInWindow();
}
@Override
public void actionPerformed(final ActionEvent e) {
addRandom();
tableaux.repaint();
}
void addRandom() {
Letter letter = new Letter(Character.toString((char) ('a' + random.nextInt(26))));
letter.setBounds(random.nextInt(WIDTH), random.nextInt(HEIGHT), 16, 16);
tableaux.add(letter);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(new SimplePaintSurface());
}
}
class Letter extends JLabel {
private Font font1;
private Font font2;
private final FontRenderContext fontRenderContext1;
private final FontRenderContext fontRenderContext2;
public Letter(final String letter) {
super(letter);
setFocusable(true);
setBackground(Color.RED);
font1 = getFont();
font2 = font1.deriveFont(48f);
fontRenderContext1 = getFontMetrics(font1).getFontRenderContext();
fontRenderContext2 = getFontMetrics(font2).getFontRenderContext();
MouseInputAdapter mouseHandler = new MouseInputAdapter() {
@Override
public void mouseEntered(final MouseEvent e) {
Letter.this.setOpaque(true);
setFont(font2);
Rectangle bounds = getBounds();
Rectangle2D stringBounds = font2.getStringBounds(getText(), fontRenderContext2);
bounds.width = (int) stringBounds.getWidth();
bounds.height = (int) stringBounds.getHeight();
setBounds(bounds);
}
@Override
public void mouseExited(final MouseEvent e) {
Letter.this.setOpaque(false);
setFont(font1);
Rectangle bounds = getBounds();
Rectangle2D stringBounds = font1.getStringBounds(getText(), fontRenderContext1);
bounds.width = (int) stringBounds.getWidth();
bounds.height = (int) stringBounds.getHeight();
setBounds(bounds);
}
};
addMouseListener(mouseHandler);
}
}
Here is an actual implementation built on @trashgods and @camickr answer:
with
addToolTip(line,toolTip)
you can add a tooltip for a specific line which will be displayed when you hover over the line. You might want to set debug=true to get a tooltip display for every location.
if you want to show a general tool tip for lines that do not have specific one you might want to add it with
addToolTip(-1,"general tool tip").
This solution is not 100% what was asked for but pretty close. With a few tweaks it should get the originally wanted result.
Source Code:
package com.bitplan.swingutil;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JTextArea;
import javax.swing.text.BadLocationException;
/**
* Answer for
* http://stackoverflow.com/questions/5957241/text-mouseover-popups-over-a-swing-jtextarea/35250911#35250911
*
* see http://stackoverflow.com/a/35250911/1497139
* a JTextArea that shows the current Position of the mouse as a tooltip
* @author wf
*
*/
public class JToolTipEventTextArea extends JTextArea {
// make sure Eclipse doesn't show a warning
private static final long serialVersionUID = 1L;
// switch to display debugging tooltip
boolean debug=false;
/**
* the map of tool tips per line
*/
public Map<Integer,String> lineToolTips=new HashMap<Integer,String>();
/**
* create me with the given rows and columns
* @param rows
* @param cols
*/
public JToolTipEventTextArea(int rows, int cols) {
super(rows,cols);
// initialize the tool tip event handling
this.setToolTipText("");
}
/**
* add a tool tip for the given line
* @param line - the line number
* @param tooltip -
*/
public void addToolTip(int line,String tooltip) {
lineToolTips.put(line,tooltip);
}
/**
* get the ToolTipText for the given mouse event
* @param event - the mouse event to handle
*/
public String getToolTipText(MouseEvent event) {
// convert the mouse position to a model position
int viewToModel =viewToModel(event.getPoint());
// use -1 if we do not find a line number later
int lineNo=-1;
// debug information
String line=" line ?";
// did we get a valid view to model position?
if(viewToModel != -1){
try {
// convert the modelPosition to a line number
lineNo = this.getLineOfOffset(viewToModel)+1;
// set the debug info
line=" line "+lineNo;
} catch (BadLocationException ble) {
// in case the line number is invalid ignore this
// in debug mode show the issue
line=ble.getMessage();
}
}
// try to lookup the tool tip - will be null if the line number is invalid
// if you want to show a general tool tip for invalid lines you might want to
// add it with addToolTip(-1,"general tool tip")
String toolTip=this.lineToolTips.get(lineNo);
// if in debug mode show some info
if (debug) {
// different display whether we found a tooltip or not
if (toolTip==null) {
toolTip="no tooltip for line "+lineNo;
} else {
toolTip="tooltip: "+toolTip+" for line "+lineNo;
}
// generally we add the position info for debugging
toolTip+=String.format(" at %3d / %3d ",event.getX(),event.getY());
}
// now return the tool tip as wanted
return toolTip;
}
}
That sounds tricky. This is just off the top of my head and probably wont get voted. But you might be able to do this.
Assuming there is some sort of HoverListener or something that you can implement, or else you will have to implement a mouse listener and build in your own wait timer. But once you get here, to a point where you know you want to popup a tooltip, you just dont know what letter/word they are on. I am pretty sure though that it is possible to get the cursor position on the screen. Then using this position you might be able to calculate where the cursor was inside the TextArea and then you can grab the character. Once you have the character/location you can also grab the whole word too without much more work. (NOTE that you want to get the "viewport" of the text area when calculating what the cursor was hovering over, if your text area has scroll bars the viewport will represent only the area visible on the screen)
Sorry for the very wordy answer, but that is the general logic I would try if I HAD to have this functionality and I know Swing does not offer it. Hopefully it makes a decent starting point.
精彩评论