javajavafxclicktabpanel

JavaFX: How to set a listener to the TabPane header onClick event


This is a weird question so I'll make my best to explain myself properly.

What I'd like is to trigger an event when a Tab in a TabPane get clicked, and by "clicked" I mean just clicked, not necessarily selected.

I already tried using the selectedProperty of the Tab, but that does call the event only if the Tab is clicked when it's not selected, not even if it's clicked when it's already selected.

The reason why I'm doing this is that I'm trying to make a collapsible tab pane that hides the content of the TabPane if you click again on the opened tab, I've already wrote the code for collapsing the TabPane and that works but... I have no idea on how to get a click event from the tab header.

I've even looked into TabPane source code too hoping that I could find the tab header container but I didn't find it there.


Solution

  • No need for a completely new skin - we can access the header nodes by lookup. Beware: implies relying on implementation details, which might change across versions.

    The (undocumented!) style id to look for is ".tab-container" - that's the only child of the TabHeaderSkin (== region for a single tab header). It contains the label, the close button (if any) and the focus marker. This "skin" keeps a reference to its tab in its properties (undocumented, of course ;)

    So the basic approach is to

    Note that the listeners have to be removed/added when the list of tabs is modified (not included in the snippet below).

    In example code:

    /** 
     * looks up the styled part of tab header and installs a mouseHandler
     * which calls the work load method.
     * 
     * @param tabPane
     */
    private void installTabHandlers(TabPane tabPane) {
        Set<Node> headers = tabPane.lookupAll(".tab-container");
        headers.forEach(node -> {
            // implementation detail: header of tabContainer is the TabHeaderSkin
            Parent parent = node.getParent();
            parent.setOnMouseClicked(ev -> handleHeader(parent));
        });
    }
    
    /**
     * Workload for tab.
     * @param tabHeaderSkin
     */
    private void handleHeader(Node tabHeaderSkin) {
        // implementation detail: skin keeps reference to associated Tab
        Tab tab = (Tab) tabHeaderSkin.getProperties().get(Tab.class);
        System.out.println("do stuff for tab: " + tab.getText());
    }