Turing85
Turing85

Reputation: 20185

How to control the JavaFX Tooltip's delay?

I was playing around with JavaFX's Tooltip. I realized that for me personally the delay between hovering over something and the tooltip actually appearing is too long. A look in the API reveals:

Typically, the tooltip is "activated" when the mouse moves over a Control. There is usually some delay between when the Tooltip becomes "activated" and when it is actually shown. The details (such as the amount of delay, etc) is left to the Skin implementation.

After some further investigation, I was not able to find any possibility to control the delay. The JavaFX CSS Reference has no information about delay time and a runtime-evaluation of getCssMetaData() did not help either.

I know that there is a way to control the tooltips manually via onMouseEntered(...) and onMouseExited(...), but is there really no other way? Or am I missing an obvious solution?

Upvotes: 36

Views: 27934

Answers (9)

Sandeep S D
Sandeep S D

Reputation: 647

It can also be done through SceneBuilder 22.0.0 by adding a Style "-fx-show-delay" to the tooltip node and setting it to say "100ms". This works for me.

Upvotes: 0

Franz Deschler
Franz Deschler

Reputation: 2574

In JavaFx 9, the tooltip delay can be set via CSS. It sounds pervert, to do this in a stylesheet, but probably this is what they mean when they say "The details (such as the amount of delay, etc) is left to the Skin implementation.".

https://docs.oracle.com/javase/9/docs/api/javafx/scene/doc-files/cssref.html#tooltip

So you can do something like this:

.tooltip {
    -fx-show-delay: 250ms;
}

Upvotes: 6

James_D
James_D

Reputation: 209408

In Java 9 and later, you can do

Tooltip tooltip = new Tooltip("A tooltip");
tooltip.setShowDelay(Duration.seconds(3));

There is also a hideDelay property, for the delay between the tooltip appearing and it being hidden again. The default values are 1 second for the showDelay and 200 milliseconds for the hideDelay.

Upvotes: 24

d_scalzi
d_scalzi

Reputation: 436

Hello, I can't comment until I reach a reputation of 50 but I wanted to correct the response given by Bruno Pado. The code he posted did not work in JDK 8u121. The issue was in which declared field it accessed. The fix is simple, change the index from 1 to 0. Working code posted below:

/**
 * <p>
 * Hack TooltipBehavior 
 */
static {
    try {
        Tooltip obj = new Tooltip();
        Class<?> clazz = obj.getClass().getDeclaredClasses()[0];
        Constructor<?> constructor = clazz.getDeclaredConstructor(
                Duration.class,
                Duration.class,
                Duration.class,
                boolean.class);
        constructor.setAccessible(true);
        Object tooltipBehavior = constructor.newInstance(
                new Duration(250),  //open
                new Duration(5000), //visible
                new Duration(200),  //close
                false);
        Field fieldBehavior = obj.getClass().getDeclaredField("BEHAVIOR");
        fieldBehavior.setAccessible(true);
        fieldBehavior.set(obj, tooltipBehavior);
    }
    catch (Exception e) {
        Logger.error(e);
    }
}

Hope this helps anyone who's looking to edit the tooltip delay while we wait for JFX9.

Upvotes: 3

Nicolas Filotto
Nicolas Filotto

Reputation: 44965

Indeed since JavaFX 2, the behavior of the tooltips is managed internally within the class Tooltip by the static field BEHAVIOR that cannot be modified using specific public methods so for now If you don't want to reinvent the wheel or wait for Java 9, the only way is by using the Reflection API as next:

/**
 * Hack allowing to modify the default behavior of the tooltips.
 * @param openDelay The open delay, knowing that by default it is set to 1000.
 * @param visibleDuration The visible duration, knowing that by default it is set to 5000.
 * @param closeDelay The close delay, knowing that by default it is set to 200.
 * @param hideOnExit Indicates whether the tooltip should be hide on exit, 
 * knowing that by default it is set to false.
 */
private static void updateTooltipBehavior(double openDelay, double visibleDuration,
    double closeDelay, boolean hideOnExit) {
    try {
        // Get the non public field "BEHAVIOR"
        Field fieldBehavior = Tooltip.class.getDeclaredField("BEHAVIOR");
        // Make the field accessible to be able to get and set its value
        fieldBehavior.setAccessible(true);
        // Get the value of the static field
        Object objBehavior = fieldBehavior.get(null);
        // Get the constructor of the private static inner class TooltipBehavior
        Constructor<?> constructor = objBehavior.getClass().getDeclaredConstructor(
            Duration.class, Duration.class, Duration.class, boolean.class
        );
        // Make the constructor accessible to be able to invoke it
        constructor.setAccessible(true);
        // Create a new instance of the private static inner class TooltipBehavior
        Object tooltipBehavior = constructor.newInstance(
            new Duration(openDelay), new Duration(visibleDuration),
            new Duration(closeDelay), hideOnExit
        );
        // Set the new instance of TooltipBehavior
        fieldBehavior.set(null, tooltipBehavior);
    } catch (Exception e) {
        throw new IllegalStateException(e);
    }
}

Upvotes: 3

Bruno Prado
Bruno Prado

Reputation: 51

Extends Tooltip or put on Application class. (Only java8)

    /**
     * <p>
     * Hack TooltipBehavior 
     */
    static {
        try {
            Tooltip obj = new Tooltip();
            Class<?> clazz = obj.getClass().getDeclaredClasses()[1];
            Constructor<?> constructor = clazz.getDeclaredConstructor(
                    Duration.class,
                    Duration.class,
                    Duration.class,
                    boolean.class);
            constructor.setAccessible(true);
            Object tooltipBehavior = constructor.newInstance(
                    new Duration(250),  //open
                    new Duration(5000), //visible
                    new Duration(200),  //close
                    false);
            Field fieldBehavior = obj.getClass().getDeclaredField("BEHAVIOR");
            fieldBehavior.setAccessible(true);
            fieldBehavior.set(obj, tooltipBehavior);
        }
        catch (Exception e) {
            Logger.error(e);
        }
    }

Upvotes: 5

Aleks Messier
Aleks Messier

Reputation: 91

I find that with the above implementation there's still a delay sometimes that I cannot explain.

The following has worked for me and removed the delay entirely:

public static void bindTooltip(final Node node, final Tooltip tooltip){
   node.setOnMouseMoved(new EventHandler<MouseEvent>(){
      @Override  
      public void handle(MouseEvent event) {
         // +15 moves the tooltip 15 pixels below the mouse cursor;
         // if you don't change the y coordinate of the tooltip, you
         // will see constant screen flicker
         tooltip.show(node, event.getScreenX(), event.getScreenY() + 15);
      }
   });  
   node.setOnMouseExited(new EventHandler<MouseEvent>(){
      @Override
      public void handle(MouseEvent event){
         tooltip.hide();
      }
   });
}

Upvotes: 9

jewelsea
jewelsea

Reputation: 159416

There is an existing feature request for this: JDK-8090477 Customizable visibility timing for Tooltip.

The feature request is currently scheduled for integration into Java 9. Attached to the issue I linked is a patch you can apply to allow you to get this functionality in earlier Java versions.

Your other option is just to create your own popup control using one of the techniques in:

Upvotes: 16

Igor Luzhanov
Igor Luzhanov

Reputation: 830

I use the next hack for this via Reflection

public static void hackTooltipStartTiming(Tooltip tooltip) {
    try {
        Field fieldBehavior = tooltip.getClass().getDeclaredField("BEHAVIOR");
        fieldBehavior.setAccessible(true);
        Object objBehavior = fieldBehavior.get(tooltip);

        Field fieldTimer = objBehavior.getClass().getDeclaredField("activationTimer");
        fieldTimer.setAccessible(true);
        Timeline objTimer = (Timeline) fieldTimer.get(objBehavior);

        objTimer.getKeyFrames().clear();
        objTimer.getKeyFrames().add(new KeyFrame(new Duration(250)));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Upvotes: 51

Related Questions