In my JavaFX app I have a TableView
with multiple columns, one of which displays data in a graphical form. To do this I have created a CanvasCell
object that creates and manages its own Canvas
to deal with the drawing. The drawing part works just fine.
I'd now like to put Tooltips
over some regions within the Canvas
/Cell
. There may be multiple Tooltips
per Cell
(which prevents me from adding the Tooltip
at the Cell
level) and they should only trigger in specific regions of the graph. However, I'm not managing to get it functioning at all. I don't seem to understand the interactions of Display Node hierarchy well enough (read "at all") to be able to place the Tooltip
anywhere where it will actually work.
Documentation for JavaFX is sparse and Google + SO has come up blank for all searches that I've tried. Is there anyone who knows how to do this sort of thing or should I just write it off as "not an option" for now.
For info, the CanvasCell
calls a draw()
function inside an extended Canvas
object on updateItem()
. The code in which I've tried to create a Tooltip
sits inside that draw()
function and looks like:
Rectangle rect = new Rectangle(leftVal, topVal, width, height);
gc.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
Tooltip tooltip = new Tooltip("Tooltip Text");
Tooltip.install(rect, tooltip);
but that code was written more in hope than anything else and doesn't generate anything useful in the interface.
If someone can point me in the right direction, I will be very grateful.
If you don't need the timing control illustrated here, you can simply install the Tooltip
on the enclosing Canvas
and leverage Shape::contains
to condition the text as shown below.
node.setOnMouseMoved(e -> {
tooltips.forEach((color, bounds) -> {
if (bounds.contains(e.getX(), e.getY())) {
tooltip.setText(color.toString());
}
});
});
As suggested here, Java 9 and later provide control over Tooltip
timing via the properties showDelay
and showDuration
.
A similar approach is illustrated here for Swing.
import javafx.application.Application;
import javafx.scene.shape.Rectangle;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.stage.Stage;
import java.util.HashMap;
import java.util.Map;
/**
* @see https://stackoverflow.com/a/53785468/230513
* @see https://stackoverflow.com/a/53753537/230513
*/
public class CanvasTooltipDemo extends Application {
@Override
public void start(Stage stage) throws Exception {
StackPane root = new StackPane();
Scene sc = new Scene(root, 400, 400);
stage.setScene(sc);
Canvas canvas = new Canvas(200, 200);
root.getChildren().add(canvas);
Map<Color, Rectangle> tooltips = new HashMap<>();
tooltips.put(Color.RED, new Rectangle(0, 0, 100, 100));
tooltips.put(Color.BLUE, new Rectangle(100, 0, 100, 100));
tooltips.put(Color.YELLOW, new Rectangle(0, 100, 100, 100));
tooltips.put(Color.GREEN, new Rectangle(100, 100, 100, 100));
GraphicsContext gc = canvas.getGraphicsContext2D();
tooltips.forEach((color, bounds) -> {
gc.setFill(color);
gc.fillRect(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
});
setToolTips(canvas, tooltips);
stage.show();
}
private void setToolTips(Node node, Map<Color, Rectangle> tooltips) {
Tooltip tooltip = new Tooltip();
Tooltip.install(node, tooltip);
node.setOnMouseMoved(e -> {
tooltips.forEach((color, bounds) -> {
if (bounds.contains(e.getX(), e.getY())) {
tooltip.setText(color.toString());
}
});
});
node.setOnMouseExited(e -> {
tooltip.hide();
});
}
public static void main(String[] args) {
Application.launch(args);
}
}