JavaFX 11: Add a graphic to a TitledPane on the right side

DJViking :

This is related to a previous question asked over three years ago. JavaFX 8 - Add a graphic to a TitledPane on the right side

I do not like this solution, and I haven't found any other solutions.

A lot has happened to JavaFX since this original question was asked for JavaFX 8. Recently JavaFX 11 was released.

I have a project using JavaFX 11. The TitledPane graphic is set with a HBox containing a single Button I want placed at the far right side of the title.

TitledPane titledPane = new TitledPane();
titledPane.setText("Title");

HBox titleBox = new HBox();
Button b1 = new Button("ClickMe!");
titleBox.getChildren().add(b1);

titledPane.setGraphic(titleBox);
titledPane.setContentDisplay(ContentDisplay.RIGHT);

Setting the TitlePane Alignment to CENTER_RIGHT will place the title to the right, but the entire title, text and graphic.

titledPane.setAlignment(Pos.CENTER_RIGHT);

I have tried experimenting with different containers in the TitledPane graphic, HBox, GridPane. Setting grow, alignment, fillWidth, etc. None works.

Without setting an artificial graphic gap, I see no viable solutions, but the hack suggested in the referenced question above. It is an ugly hack. For multiple TitledPanes in an Acrodion. However the Button is not placed at the exact end, there is still more room to the right on the title.

    primaryStage.setOnShown(e -> {
        for (TitledPane titledPane : panes) {
            Node titleRegion = titledPane.lookup(".title");
            Insets padding = ((StackPane) titleRegion).getPadding();
            double graphicWidth = titledPane.getGraphic().getLayoutBounds().getWidth();
            double arrowWidth = titleRegion.lookup(".arrow-button").getLayoutBounds().getWidth();
            double labelWidth = titleRegion.lookup(".text").getLayoutBounds().getWidth();
            double nodesWidth = graphicWidth + padding.getLeft() + padding.getRight() + arrowWidth + labelWidth;
            titledPane.graphicTextGapProperty().bind(titledPane.widthProperty().subtract(nodesWidth));
        }
    });
Zephyr :

I am not sure I fully understand your goal, but I believe you want the TitledPane to have both a Label and a Button with the Label left-aligned and the Button right-aligned, correct?

In order to do that, does require a little bit of a workaround and use of JavaFX's containers. First of all, no need to set a title for the TitledPane itself; we will add a Label to our graphic to serve as the title.

In the example below, we will use a HBox to hold all of the nodes we want within the TitledPane. Within the HBox, we add a Region that is set to always grow within the HBox. This forces the Button all the way to the right.

Then, since the TitledPane has a semi-fixed max width for its graphic, we need to bind our HBox to the width of the TitledPane, with padding to avoid overlapping the "expand" arrow.

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Simple interface
        VBox root = new VBox(5);
        root.setPadding(new Insets(10));
        root.setAlignment(Pos.CENTER);

        // Create the TitlePane
        TitledPane titledPane = new TitledPane();
        titledPane.setAlignment(Pos.CENTER);

        // Create HBox to hold our Title and button
        HBox contentPane = new HBox();
        contentPane.setAlignment(Pos.CENTER);

        // Set padding on the left side to avoid overlapping the TitlePane's expand arrow
        // We will also pad the right side
        contentPane.setPadding(new Insets(0, 10, 0, 35));

        // Now, since the TitlePane's graphic node generally has a fixed size, we need to bind our
        // content pane's width to match the width of the TitledPane. This will account for resizing as well
        contentPane.minWidthProperty().bind(titledPane.widthProperty());

        // Create a Region to act as a separator for the title and button
        HBox region = new HBox();
        region.setMaxWidth(Double.MAX_VALUE);
        HBox.setHgrow(region, Priority.ALWAYS);

        // Add our nodes to the contentPane
        contentPane.getChildren().addAll(
                new Label("Titles Are Cool"),
                region,
                new Button("Click me!")
        );

        // Add the contentPane as the graphic for the TitledPane
        titledPane.setGraphic(contentPane);

        // Add the pane to our root layout
        root.getChildren().add(titledPane);

        // Show the Stage
        primaryStage.setWidth(400);
        primaryStage.setHeight(300);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}

The Result:

Screenshot

This is a fairly clean solution and I would not consider it to be a "hack". Just using the containers and structures of JavaFX as they were intended.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=115234&siteId=1