Search code examples
javafxtooltipjavafx-8

How to dynamically control tooltip display in a JavaFX application


I am looking for a way to dynamically control the tooltip display in a JavaFX application, based on some global property. Say suppose in my application there are 1000 nodes which have tooltip installed. I have a Boolean property to set the tooltips turn on/off. How can I achieve this in a more feasible way? Just to let you know, I am aware of tooltip install/uninstall features. I don't want to do this for all the 1000 nodes every time user toggles the setting property.

My initial thought is to extend the Tooltip control and set some event filter for "onShowing" which consumes the event if the setting property is false. But this is not stopping the tooltip to display. The tooltip is displayed even if I consume the event.

Alternatively I tried setting the event dispatcher as well. But that too didn't worked. Below is the code that I tried to work on.

Any help regarding this is much appreciated.

import javafx.event.Event;
import javafx.event.EventDispatcher;
import javafx.scene.control.Tooltip;
import javafx.stage.WindowEvent;

public class CustomTooltip extends Tooltip {

    public static boolean SHOW_TOOLTIP = false;

    public CustomTooltip(String txt){
        super(txt);

        // Approach #1
        addEventFilter(WindowEvent.WINDOW_SHOWING, e->{
            if (!SHOW_TOOLTIP) {
                e.consume();
            }
        });

        // Approach #2
        //setEventDispatcher();
    }

    private void setEventDispatcher() {
        final EventDispatcher oed = getEventDispatcher();
        final EventDispatcher ned = (event, tail) -> {
            Event r = null;
            if (!SHOW_TOOLTIP) {
                event.consume();
            } else{
                r = oed.dispatchEvent(event, tail);
            }
            return r;
        };
        setEventDispatcher(ned);
    }
}

Below is the complete example/demo that demonstrates my requirement.

import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventDispatcher;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

public class TooltipDisableDemo extends Application {
    public static boolean SHOW_TOOLTIP = false;


    @Override
    public void start(Stage stage) throws Exception {
        StackPane root = new StackPane();
        Scene sc = new Scene(root, 700, 700);
        stage.setScene(sc);
        stage.setTitle("Tooltips Disable Demo");
        stage.show();

        CheckBox showToolTip = new CheckBox("Enable tooltip displaying");
        showToolTip.selectedProperty().addListener((obs, old, show) -> SHOW_TOOLTIP = show);
        showToolTip.setSelected(true);

        TabPane tabPane = new TabPane();
        for (int i = 1; i < 11; i++) {
            Tab tab = new Tab("Tab " + i);
            tab.setClosable(false);
            FlowPane fp = new FlowPane();
            fp.setPadding(new Insets(10));
            fp.setHgap(15);
            fp.setVgap(15);
            for (int j = 1; j < 51; j++) {
                StackPane sp = new StackPane();
                sp.setStyle("-fx-background-color: gray");
                sp.setPadding(new Insets(0,5,0,5));
                sp.getChildren().add(new Label("SP T"+i+" - "+j));
                Tooltip.install(sp, new CustomTooltip("This is stack pane " + j + " in Tab " + i));

                Button btn = new Button("Button T"+i+" - "+j);
                btn.setTooltip(new CustomTooltip("This is button " + j + " in Tab " + i));
                fp.getChildren().addAll(sp, btn);
            }
            tab.setContent(fp);
            tabPane.getTabs().add(tab);
        }
        VBox.setVgrow(tabPane, Priority.ALWAYS);

        VBox vb = new VBox();
        vb.setSpacing(10);
        vb.setPadding(new Insets(10));
        vb.getChildren().addAll(showToolTip,tabPane);
        root.getChildren().add(vb);
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

    /**
     * Custom tooltip implementation.
     */
    class CustomTooltip extends Tooltip {

        public CustomTooltip(String txt){
            super(txt);

            // Approach #1
            addEventFilter(WindowEvent.WINDOW_SHOWING, e->{
                if (!SHOW_TOOLTIP) {
                    e.consume();
                }
            });

            // Approach #2
            //setEventDispatcher();
        }

        private void setEventDispatcher() {
            final EventDispatcher oed = getEventDispatcher();
            final EventDispatcher ned = (event, tail) -> {
                Event r = null;
                if (!SHOW_TOOLTIP) {
                    event.consume();
                } else{
                    r = oed.dispatchEvent(event, tail);
                }
                return r;
            };
            setEventDispatcher(ned);
        }
    }
}

Solution

  • Below is the final working demo as per kleopatra's suggestion. I am overriding the protected method show() and conditionally calling the super method to show the tooltip.

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.layout.FlowPane;
    import javafx.scene.layout.Priority;
    import javafx.scene.layout.StackPane;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    public class TooltipDisableDemo extends Application {
        public static boolean SHOW_TOOLTIP = false;
    
    
        @Override
        public void start(Stage stage) throws Exception {
            StackPane root = new StackPane();
            Scene sc = new Scene(root, 700, 700);
            stage.setScene(sc);
            stage.setTitle("Tooltips Disable Demo");
            stage.show();
    
            CheckBox showToolTip = new CheckBox("Enable tooltip displaying");
            showToolTip.selectedProperty().addListener((obs, old, show) -> SHOW_TOOLTIP = show);
            showToolTip.setSelected(true);
    
            TabPane tabPane = new TabPane();
            for (int i = 1; i < 11; i++) {
                Tab tab = new Tab("Tab " + i);
                tab.setClosable(false);
                FlowPane fp = new FlowPane();
                fp.setPadding(new Insets(10));
                fp.setHgap(15);
                fp.setVgap(15);
                for (int j = 1; j < 51; j++) {
                    StackPane sp = new StackPane();
                    sp.setStyle("-fx-background-color: gray");
                    sp.setPadding(new Insets(0, 5, 0, 5));
                    sp.getChildren().add(new Label("SP T" + i + " - " + j));
                    Tooltip.install(sp, new CustomTooltip("This is stack pane " + j + " in Tab " + i));
    
                    Button btn = new Button("Button T" + i + " - " + j);
                    btn.setTooltip(new CustomTooltip("This is button " + j + " in Tab " + i));
                    fp.getChildren().addAll(sp, btn);
                }
                tab.setContent(fp);
                tabPane.getTabs().add(tab);
            }
            VBox.setVgrow(tabPane, Priority.ALWAYS);
    
            VBox vb = new VBox();
            vb.setSpacing(10);
            vb.setPadding(new Insets(10));
            vb.getChildren().addAll(showToolTip, tabPane);
            root.getChildren().add(vb);
        }
    
        public static void main(String[] args) {
            Application.launch(args);
        }
    
        /**
         * Custom tooltip implementation.
         */
        class CustomTooltip extends Tooltip {
            public CustomTooltip() {
                super();
            }
    
            public CustomTooltip(String txt) {
                super(txt);
            }
    
            @Override
            protected void show() {
                if (SHOW_TOOLTIP) {
                    super.show();
                }
            }
        }
    }