Skip to content

JavaFX Add custom button to HTMLEditor toolbar at position

I have an HTMLEditor where I want to add a button to the toolbar that inserts a table into the editor. I can insert the button, but I can’t get it in the correct position. I want it to appear to the right of the list buttons like so:

enter image description here

But instead I can only get it to add to the front of the toolbar

enter image description here

My attempts to insert at a certain position in the toolbar have failed. For example, when I try

bar.getItems().add(6, tableButton);

this throws an error, and when I print

bar.getItems().size();

I get 0, which means I’m doing it wrong because obviously there’s some toolbar buttons.

How can I insert the table button at the correct position, to the left of the divider that’s currently to the right of the ordered list button.

Here’s my code, which is set as the controller for the HTMLEditor in Scene Builder

package editorgroup;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ToolBar;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.web.HTMLEditor;
import main.Main;


public class EditorGroupController {

    @FXML
    private HTMLEditor htmlEditor;
    
    // Reference to the main application.
    private Main mainApp;
    public void setMainApp(Main mainApp) {
        this.mainApp = mainApp;
    }


    public EditorGroupController() {
    }

    // This method is automatically called after the fxml file has been loaded.
    @FXML
    private void initialize() {
        
        // add a custom button to the top toolbar.
        Node node = htmlEditor.lookup(".top-toolbar");
        if (node instanceof ToolBar) {
            ToolBar bar = (ToolBar) node;

            ClassLoader classLoader = getClass().getClassLoader();
            String absolutePath = "file://" + classLoader.getResource("table_icon.png").getPath();
            ImageView graphic = new ImageView(new Image(absolutePath, 20, 20, true, true));
            Button tableButton = new Button("", graphic);
            bar.getItems().add(tableButton);
            
            tableButton.setOnAction(new EventHandler<ActionEvent>() {
                public void handle(ActionEvent arg0) {
                    htmlEditor.setHtmlText("<table><tbody><tr><td>HEY!</td></tr></tbody>");
                }
            });
        }
    }   
}

Answer

The ToolBar is constructed in the HTMLEditorSkin of HTMLEditor but it is not populated on construction.
It is populated on the first invocation of layoutChildren.
Therefore you need to modify the toolbar after at least one layout pass:

import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.control.Button;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.VBox;
import javafx.scene.web.HTMLEditor;
import javafx.stage.Stage;

public class HtmlEditorExample extends Application {

    @Override
    public void start(Stage primaryStage) {

        HTMLEditor htmlEditor = new HTMLEditor();
        ToolBar bar = null;
        Node node = htmlEditor.lookup(".top-toolbar");
        if (node instanceof ToolBar) {
            bar = (ToolBar) node;
            System.out.println( "Size before layout pass: "+bar.getItems().size());
        }

        VBox vBox = new VBox(htmlEditor);
        primaryStage.setScene(new Scene(vBox));
        primaryStage.show(); //invokes layout pass

        if (bar != null) {
            System.out.println( "Size after layout pass: "+bar.getItems().size());
            bar.getItems().add(6, new Button("My Button"));
        }
    }

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