Xylian
Xylian

Reputation: 241

How to hide TabPane content on Tab clicked in JavaFX

Here is a code:

package tabpane;

import javafx.application.*;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;

public class HideShowTabContentOnClicked extends Application {

    public static void main(String[] args) {

        launch(args);
    }

    private BorderPane createContent() {

        BorderPane borderPane = new BorderPane();

        TabPane tabPane = new TabPane();
        tabPane.setSide(Side.LEFT);

        StackPane stackPane = new StackPane();
        stackPane.setStyle("-fx-background-color: lightblue");
        stackPane.setMinWidth(200);

        Tab firstTab = new Tab("First");
        firstTab.setClosable(false);
        firstTab.setContent(stackPane);
        firstTab.selectedProperty().addListener((observable, oldValue, newValue) -> {
            if (!newValue) {
                firstTab.setContent(null);
            } else {
                firstTab.setContent(stackPane);
            }
        });

        Tab secondTab = new Tab("Second");
        StackPane stackPane2 = new StackPane();
        stackPane2.setStyle("-fx-background-color: yellow");
        secondTab.setContent(stackPane2);
        secondTab.setClosable(false);

        secondTab.selectedProperty().addListener((observable, oldValue, newValue) -> {
            if (!newValue) {
                secondTab.setContent(null);
            } else {
                secondTab.setContent(stackPane2);
            }
        });

        StackPane center = new StackPane();
        center.setStyle("-fx-background-color: cyan");

        borderPane.setCenter(center);

        tabPane.getTabs().addAll(firstTab, secondTab);
        borderPane.setLeft(tabPane);
        return borderPane;
    }

    @Override
    public void start(Stage stage) throws Exception {

        stage.setScene(new Scene(createContent()));
        stage.setMaximized(true);
        stage.show();
    }
}

Here I tried to solve a problem by using selectedProperty() by setting content to null, but it doesn't working, I want to make Tab like toggle button so that when I click on it showed and hide TabPanes content.

Before

And when clicked

As an example I want to implement TabPane like Intellij IDEA Tool Buttons (like "Project", "Structure" Tool Buttons etc).

Upvotes: 4

Views: 1500

Answers (2)

AshFTW
AshFTW

Reputation: 141

I have came up with this solution:

AtomicReference<Tab> currentTab = new AtomicReference<>(tabPane.getSelectionModel().getSelectedItem());
AtomicReference<Tab> lastTab = new AtomicReference<>(null);
tabPane.setOnMouseReleased(event -> {
    // Check if current node is actually tab
    Node n = event.getPickResult().getIntersectedNode();
    while (n != null && !(n.getStyleClass().contains("headers-region"))) {
        n = n.getParent();
    }
    if (n == null)
        return;

    lastTab.set(currentTab.get());
    currentTab.set(tabPane.getSelectionModel().getSelectedItem());

    if (currentTab.get() == lastTab.get()) {
        // Hide
        tabPane.setPrefSize(28, 28);
        //tabPane.getSelectionModel().clearSelection(); // notify selection model
        currentTab.set(null);
    } else {
        // Show
        tabPane.setPrefSize(-1,-1);
        currentTab.set(tabPane.getSelectionModel().getSelectedItem());
    }
});

First of all, I have added mouse event to the tabPane. Inside this mouse event, check if node under cursor is actually Tab node. If it is, do some logic to identify what user is trying to do: hide or show. Hiding is a bit tricky, so I ended up with setting preferred size of TabPane to 28 px wide.

I have also tried to notify selection model with an empty newValue:

tabPane.getSelectionModel().clearSelection();

But this it is not working properly. Calling select(-1) should call clearSelection(), but behavior is different somehow.

When I select another tab after calling clearSelection(), selection model handler called with oldValue == null, that possibly does not update internal index and tab does not swithes to selected one.

Upvotes: 0

kozmo
kozmo

Reputation: 4471

If you are going to keep your content into StackPane, you can bind stackPane.visibleProperty() with toggleButton.selectedProperty():

stackPane.visibleProperty()
             .bind(Bindings.when(toggleButton.selectedProperty())
                           .then(false)
                           .otherwise(true)
             );

in this exampl: toggleButton.isSelected() --> !stackPane.isVisible() and !toggleButton.isSelected() --> stackPane.isVisible(), or listen ToggleButton's events:

// toggleButton.setOnAction(e ->{                      //new .setOnAction() -> Override previous
toggleButton.addEventHandler(ActionEvent.ACTION, e ->{ //can add any quantity for your needs
        if(toggleButton.isSelected())
             stackPane.setVisible(false);
        else stackPane.setVisible(true);
    });

But the problem is instead of toggle button I want to use Tab, so that it behaves like toggle button. i.e. when click "First Tab" in my example code if content visible it should be invisible and vice versa. I mean only tabs should be shown

I found solution.Tab does not have click-handler... but

Tab tab = new Tab();
tab.setContent(stackPane);
Label lable = new Label("Label");    //create Label
tab.setGraphic(lable);               //set Lable as Graphic to Tab
lable.setOnMouseClicked(event ->{    //setOnMouseClicked, for example
        if(stackPane.isVisible()){
            stackPane.setVisible(false);
        }else{
            stackPane.setVisible(true);
        }
});

, you can use Label(for example) as Tab-text and add setOnMouseClicked()-handler to Label. You can use any Node with Handler/ActionListener -> It's up to you.

For example, you can use CheckBox to show/hide StackPane, and Tab text (you can combine FXML and Java-code to produce graphics):

enter image description here

Tab tab = new Tab("Tab2");          //Tab with Text
tab.setContent(stackPane);          
CheckBox checkBox = new CheckBox(); //create CheckBox 
tab.setGraphic(checkBox);           //set CheckBox as Graphic to Tab
stackPane.visibleProperty()
            .bind(Bindings.when(checkBox.selectedProperty())
                    .then(false)
                    .otherwise(true)
            );

or

@FXML
private Tab tab;
// ...
tab.setGraphic(checkBox); 
// ...

Upvotes: 1

Related Questions