{"id":999,"date":"2016-08-09T12:24:50","date_gmt":"2016-08-09T10:24:50","guid":{"rendered":"http:\/\/www.pellissier.co.za\/hermien\/?p=999"},"modified":"2016-08-09T12:24:50","modified_gmt":"2016-08-09T10:24:50","slug":"javafx-and-netbeans-dynamically-loading-windows","status":"publish","type":"post","link":"https:\/\/www.pellissier.co.za\/hermien\/?p=999","title":{"rendered":"JavaFX and NetBeans: Dynamically Loading Windows"},"content":{"rendered":"<p>The Swing-based window system in the NetBeans Platform has the concept of a window that is loaded at runtime. These windows derive from the class TopComponent and are registered using annotations, and are created using a handy wizard in the NetBeans IDE. Today I am going to show you how to do something similar with JavaFX components, using the <a href=\"http:\/\/wiki.netbeans.org\/DevFaqLookupDefault\" target=\"_blank\">default lookup<\/a> of the NetBeans platform. My aim is to load an FXML file and display it on a tabbed pane. <\/p>\n<p>I am using the source code from my <a href=\"http:\/\/www.pellissier.co.za\/hermien\/?p=950\" target=\"_blank\">previous blog entry<\/a> as a starting point because I already had it handy. That main fxml file already had a tabPane, which is what I require in the GUI.<\/p>\n<figure id=\"attachment_994\" aria-describedby=\"caption-attachment-994\" style=\"width: 630px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.pellissier.co.za\/hermien\/wp-content\/uploads\/sites\/2\/2016\/07\/window031.png\"><img loading=\"lazy\" src=\"http:\/\/www.pellissier.co.za\/hermien\/wp-content\/uploads\/sites\/2\/2016\/07\/window031.png\" alt=\"Window with basic menu\" width=\"640\" height=\"480\" class=\"size-full wp-image-994\" srcset=\"https:\/\/www.pellissier.co.za\/hermien\/wp-content\/uploads\/sites\/2\/2016\/07\/window031.png 640w, https:\/\/www.pellissier.co.za\/hermien\/wp-content\/uploads\/sites\/2\/2016\/07\/window031-300x225.png 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><figcaption id=\"caption-attachment-994\" class=\"wp-caption-text\">Application from the previous blog entry<\/figcaption><\/figure>\n<p>I created a package called za.co.pellissier.javafxwindowsystem.api where the public API of my JavaFXWindowSystem will be located. To be able to add a component to the tabbed pane, the text to be displayed on the tab as well as a javafx.scene.Node object is needed. So I created an interface that windows will need to implement:<\/p>\n<p>[java]package za.co.pellissier.javafxwindowsystem.api;<\/p>\n<p>import javafx.scene.Node;<\/p>\n<p>public interface IJavaFXWindow {<\/p>\n<p>    public Node load();<\/p>\n<p>    String getWindowName();<br \/>\n}[\/java]<\/p>\n<p>There is a class called FXMLLoader that is able to load an fxml file and return a Node object representing its content. To save future implementers of this API some effort, here is an abstract class that handles the loading:<\/p>\n<p>[java]package za.co.pellissier.javafxwindowsystem.api;<\/p>\n<p>import java.io.IOException;<br \/>\nimport java.net.URL;<br \/>\nimport java.util.logging.Level;<br \/>\nimport java.util.logging.Logger;<br \/>\nimport javafx.fxml.FXMLLoader;<br \/>\nimport javafx.scene.Node;<\/p>\n<p>public abstract class AJavaFXWindow implements IJavaFXWindow {<\/p>\n<p>    private final String fxmlFileName;<\/p>\n<p>    public AJavaFXWindow(String fxmlName) {<br \/>\n        fxmlFileName = fxmlName;<br \/>\n    }<\/p>\n<p>    @Override<br \/>\n    public Node load() {<br \/>\n        try {<br \/>\n            URL resource = getClass().getResource(fxmlFileName);<br \/>\n            return (Node) FXMLLoader.load(resource);<br \/>\n        } catch (IOException ex) {<br \/>\n            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);<br \/>\n        }<br \/>\n        return null;<br \/>\n    }<br \/>\n}[\/java]<\/p>\n<p>That is all that there is to the API, for now. \ud83d\ude42 <\/p>\n<p>Note: Remember to export the package (set it to public) if you want it to be visible to other modules!<\/p>\n<p>So lets look at how it is used by the window system. In my MainWindowController class (the JavaFX controller for the main fxml file), I added this method:<\/p>\n<p>[java]    private void loadWindows() {<br \/>\n        Collection<? extends IJavaFXWindow> windows =<br \/>\n                Lookup.getDefault().lookupAll(IJavaFXWindow.class);<br \/>\n        for (IJavaFXWindow window : windows) {<br \/>\n            Node win = window.load();<br \/>\n            String name = window.getWindowName();<br \/>\n            tabPane.getTabs().add(new Tab(name, win));<br \/>\n        }<br \/>\n    }[\/java]<\/p>\n<p>I called this method in the initialize() method after the menu items are loaded.<\/p>\n<p>All that remains is to see how a new window can be added to the system. First, lets create a new module called JavaFXTestWindow to contain the new window. It needs dependencies on Lookup API and JavaFXWindowSystem (the module where the API is defined). <\/p>\n<p>In the JavaFXTestWindow module, I created a new fxml file called testwindow.fxml using SceneBuilder, and generated a controller class using the NetBeans IDE. Now for the most important step&#8230; implementing that brand new API!<\/p>\n<p>[java]package za.co.pellissier.testwindow;<\/p>\n<p>import org.openide.util.lookup.ServiceProvider;<br \/>\nimport za.co.pellissier.javafxwindowsystem.api.AJavaFXWindow;<br \/>\nimport za.co.pellissier.javafxwindowsystem.api.IJavaFXWindow;<\/p>\n<p>@ServiceProvider(service = IJavaFXWindow.class)<br \/>\npublic class MyWindow extends AJavaFXWindow implements IJavaFXWindow {<\/p>\n<p>    public MyWindow() {<br \/>\n        super(&#8220;testwindow.fxml&#8221;);<br \/>\n    }<\/p>\n<p>    @Override<br \/>\n    public String getWindowName() {<br \/>\n        return &#8220;Test Me!&#8221;;<br \/>\n    }<br \/>\n}[\/java]<\/p>\n<p>The most important line is the @ServiceProvider annotation &#8211; if you leave that out, the default lookup will not be able to find the window!<\/p>\n<p>After cleaning and building, here is the result!<\/p>\n<figure id=\"attachment_1015\" aria-describedby=\"caption-attachment-1015\" style=\"width: 630px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.pellissier.co.za\/hermien\/wp-content\/uploads\/sites\/2\/2016\/08\/window04.png\"><img loading=\"lazy\" src=\"http:\/\/www.pellissier.co.za\/hermien\/wp-content\/uploads\/sites\/2\/2016\/08\/window04.png\" alt=\"FXML file loaded\" width=\"640\" height=\"480\" class=\"size-full wp-image-1015\" srcset=\"https:\/\/www.pellissier.co.za\/hermien\/wp-content\/uploads\/sites\/2\/2016\/08\/window04.png 640w, https:\/\/www.pellissier.co.za\/hermien\/wp-content\/uploads\/sites\/2\/2016\/08\/window04-300x225.png 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><figcaption id=\"caption-attachment-1015\" class=\"wp-caption-text\">FXML file loaded<\/figcaption><\/figure>\n<p>There are certainly still improvements to be made to this implementation. It only emulates a single mode in the NetBeans Platform Window System. And it requires the user to manually create an additional class. But it is a good start! \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Swing-based window system in the NetBeans Platform has the concept of a window that is loaded at runtime. These windows derive from the class TopComponent and are registered using annotations, and are created using a handy wizard in the NetBeans IDE. Today I am going to show you how to do something similar with &#8230; <a title=\"JavaFX and NetBeans: Dynamically Loading Windows\" class=\"read-more\" href=\"https:\/\/www.pellissier.co.za\/hermien\/?p=999\" aria-label=\"More on JavaFX and NetBeans: Dynamically Loading Windows\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false},"categories":[8,3,5],"tags":[],"jetpack_featured_media_url":"","jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p1v8WL-g7","_links":{"self":[{"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=\/wp\/v2\/posts\/999"}],"collection":[{"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=999"}],"version-history":[{"count":20,"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=\/wp\/v2\/posts\/999\/revisions"}],"predecessor-version":[{"id":1020,"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=\/wp\/v2\/posts\/999\/revisions\/1020"}],"wp:attachment":[{"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=999"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=999"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pellissier.co.za\/hermien\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=999"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}