Skip to main content

Embedding JavaFX Scene Builder 2.0 ea in NetBeans - Part 2

Posted by sven on December 7, 2013 at 12:19 PM PST

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 part of this series?)!

Ok, having done a bit of magic to Scene Builder 2.0 ea in the last part of this series, let's go for some more integration. Goal of this part is to get the hierarchy view from Scene Builder running inside the NetBeans navigator view - looking like this



First thing you will need is to publicize the EditorController so it will be available in the global lookup and can be used from the upcoming NavigatorPanel. The following code does this.

    private MultiViewElementCallback callback;
    private DataObject dao;
    private  EditorController editorController;
   
    private Lookup lkp;
    private InstanceContent ic;
   
    private JFXPanel jfxPanel;

    public SBFxmlMultiViewElement(Lookup lookup) {
        Platform.setImplicitExit(false);
        dao = lookup.lookup(DataObject.class);
        assert dao != null;
        ic = new InstanceContent();
        lkp = new AbstractLookup(ic);
    }

    @Override
    public JComponent getVisualRepresentation() {
        if (null == jfxPanel) {
            jfxPanel = new JFXPanel();
            try {
                final File fxmlFile = FileUtil.toFile(dao.getPrimaryFile());
                final String fxmlText = readContentFromFile(fxmlFile);
                final URL fxmlLocation = fxmlFile.toURI().toURL();
                Platform.runLater(new Runnable() {

                    @Override
                    public void run() {
                        try {
                            editorController = new EditorController();
                            Node node = new AbstractNode(Children.LEAF, Lookups.fixed(editorController));
                            ic.add(node);
                            ContentPanelController contentPanelController = new ContentPanelController(editorController);
                            final BorderPane pane = new BorderPane();
                            pane.setCenter(contentPanelController.getPanelRoot());
                            Scene scene = new Scene(pane);
                            jfxPanel.setScene(scene);
                            editorController.setFxmlTextAndLocation(fxmlText, fxmlLocation);
                        } catch (IOException ex) {
                            Exceptions.printStackTrace(ex);
                        }
                    }
                });


            } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
        return jfxPanel;
    }

    @Override
    public Lookup getLookup() {
        return lkp;
    }

Since we do not want the Scene Builder navigator view to show up in the normal XML based MultiViewElement, it is necessary to create a replacment NavigatorLookupHint. The following code (living in the same file as above code) creates a hint for the a synthetic content type called "text/scenebuilder" - I am sure there is a better content type name (names are hard), but it does what is necessary.

class SBTypeLookupHint implements NavigatorLookupHint {
    public String getContentType () {
        return "text/scenebuilder";
    }
}

Now add an instance of this hint to the lookup. Change the constructor as follows:

    public SBFxmlMultiViewElement(Lookup lookup) {
        Platform.setImplicitExit(false);
        dao = lookup.lookup(DataObject.class);
        assert dao != null;
        ic = new InstanceContent();
        ic.add(new SBTypeLookupHint());
        lkp = new AbstractLookup(ic);
    }

Based on this we can now create the NavigatorPanel

@NavigatorPanel.Registration(displayName = "Hierarchy", mimeType = "text/scenebuilder")
public class FxmlNavigator implements NavigatorPanel, LookupListener {

    private JFXPanel panel = new JFXPanel();
    private Lookup lkp;
    private InstanceContent ic;
    private Lookup.Result editorControllerResult;

    public FxmlNavigator() {
        ic = new InstanceContent();
        lkp = new AbstractLookup(ic);
    }

    @Override
    public String getDisplayName() {
        return "Hierarchy";
    }

    @Override
    public String getDisplayHint() {
        return "Hierarchy Hint";
    }

    @Override
    public JComponent getComponent() {
        return panel;
    }

    @Override
    public void panelActivated(Lookup lkp) {
        editorControllerResult = lkp.lookupResult(EditorController.class);
        editorControllerResult.addLookupListener(this);
        resultChanged(new LookupEvent(editorControllerResult));
    }

    @Override
    public void panelDeactivated() {
    }

    @Override
    public Lookup getLookup() {
        return lkp;
    }

    @Override
    public void resultChanged(LookupEvent le) {
        final Optional optionalController = editorControllerResult.allInstances().stream().findFirst();
        if (optionalController.isPresent()) {
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                    HierarchyPanelController h = new HierarchyPanelController(optionalController.get());
                    final BorderPane pane = new BorderPane();
                    pane.setCenter(h.getPanelRoot());
                    Scene scene = new Scene(pane);
                    panel.setScene(scene);
                }
            });
        }
    }

}

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 legal disclaimer from Oracle keeps me from redistributing the necessary SceneBuilderKit.jar. Sorry, you have to do it on your own. Get the sources from Bitbucket and try it out. The one thing you have to do is get Scene Builder 2.0 Early Access and put SceneBuilderKit.jar into the right directory (hint there is a file indicating, which one it is).

Stay tuned for the next part of this series, showing further integration steps - maybe palette or property sheet.

AttachmentSize
Bildschirmfoto_2013-11-28_um_22.06.11.png331.77 KB

Comments

Very, very cool, Sven! This is a great start, and the ...

Very, very cool, Sven!

This is a great start, and the Scene Builder team just can't wait to see your next steps.
As you can imagine, we eat our own dog food, so the team uses Scene Builder to develop Scene Builder UI… and also uses NetBeans. As soon as you have the Inspector also integrated, we are likely to give a shot to your prototype for our own development!

You might be aware that we have just open sourced Scene Builder as part of the OpenJFX project.
This is under a BSD-style license, which should now allow you to include the SceneBuilderKit.jar as you wish.
That code is under rt/apps/scenebuilder/SceneBuilderKit/. You can build it simply with:
$ ant -Dplatforms.JDK_1.8.home=/your/jdk1.8.0/location/ jar

Keep up the good work!
-Simon.