Skip to main content

Embedding JavaFX Scene Builder 2.0 in NetBeans - Part 4

Posted by sven on July 28, 2014 at 2:29 PM PDT

Tired of JavaFX Scene Builder being run in a separate process? Fed up with no real integration between your favorite IDE and JavaFX Scene Builder? There may be a solution heading towards you. Follow this small series of blog entries to join me on my journey towards an embedded JavaFX Scene Builder in NetBeans.

Welcome back (you did read the first three parts of this series?)!

Ok, having done a bit of magic to Scene Builder 2.0 in the first two part of this series, let's go for some more integration. Goal of this part is to get change detection and undo/redo support working.

Change detection is important to get the "Save" feature of NetBeans to work. Because the integration is actually based on reusing the fxml as a FXMLDataObject it is necessary to detect a change done in the Scene Builder and apply the result back to the FXMLDataObject. But how to detect the change. This can be done using the observable property revision from the JobManager which in turn can be retrieved from the EditorController. The JobManager tracks every action applied to the scene as a "Job" and increments the revision with each change. With this change detection can be done with the following code fragment

editorController.getJobManager().revisionProperty().addListener((ov, oldValue, newValue) -> {
    try {
        String updatedFXMLText = editorController.getFxmlText();
        EditorCookie editorCookie = dao.getLookup().lookup(EditorCookie.class);
        editorCookie.getDocument().remove(0, editorCookie.getDocument().getLength());
        editorCookie.getDocument().insertString(0, updatedFXMLText, null);
    } catch (BadLocationException ex) {
        Exceptions.printStackTrace(ex);
    }
});

This just reads the complete FXML as a pure text from Scene Builder and puts it back into the document managed by NetBeans. This triggers a change on the FXMLDataObject side of the world and the save logic is active.

Knowing about the JobManager makes undo/redo integration quite simple. What we need from NetBeans perspective is an UndoRedo implementation, which is returned from the appropriate method of our SBFxmlMultiViewElement

    @Override
    public UndoRedo getUndoRedo() {
        return null != undoRedo ? undoRedo : UndoRedo.NONE;
    }

What about the required implementation? Looking at the API's of UndoRedo and JobManager they are quite similiar. So just creating a SceneBuilderUndoRedoBridge should be fairly simple:

public class SceneBuilderUndoRedoBridge implements UndoRedo {

    private final JobManager manager;
    private final ChangeSupport changeSupport = new ChangeSupport(this);

    SceneBuilderUndoRedoBridge(JobManager manager) {
        this.manager = manager;
        this.manager.revisionProperty().addListener((observable) -> {
            EventQueue.invokeLater(() -> {
                changeSupport.fireChange();
            });
        });
    }

    @Override
    public boolean canUndo() {
        return manager.canUndo();
    }

    @Override
    public boolean canRedo() {
        return manager.canRedo();
    }

    @Override
    public void undo() throws CannotUndoException {
        Platform.runLater(()-> manager.undo());
    }

    @Override
    public void redo() throws CannotRedoException {
        Platform.runLater(()-> manager.redo());
    }

    @Override
    public void addChangeListener(ChangeListener cl) {
        changeSupport.addChangeListener(cl);
    }

    @Override
    public void removeChangeListener(ChangeListener cl) {
        changeSupport.removeChangeListener(cl);
    }

    @Override
    public String getUndoPresentationName() {
        return manager.getUndoDescription();
    }

    @Override
    public String getRedoPresentationName() {
        return manager.getRedoDescription();
    }

}

Now just make sure that the new UndoRedo support is availble by adding just one more line of code to the getVisualRepresentation()method of SBFxmlMultiViewElement

    undoRedo = new SceneBuilderUndoRedoBridge(editorController.getJobManager());

That's it - change detection and undo/redo integration working!

I am sure the code can be improved - just let me know and create a pull request ;-)

Now you ask - can I download this magic plugin somewhere? The answer is simple: Get it from the NetBeans Plugin Portal by downloading it from here or install it via your Plugin Manager from inside NetBeans.

What you will need in addition to the plugin is a fairly recent (latest) JDK8_u20 ea build. There are some issues with DnD and JavaFX-Swing-Integration which have been fixed in latest builds.

Stay tuned for the next part of this series, showing .. whatever I come up with next. Any feature requests? Head over to NbSceneBuilder and let me know.