{"id":827,"date":"2013-08-03T22:12:03","date_gmt":"2013-08-03T20:12:03","guid":{"rendered":"https:\/\/mihosoft.eu\/?p=827"},"modified":"2014-05-23T18:07:12","modified_gmt":"2014-05-23T16:07:12","slug":"part-5-custom-node-content","status":"publish","type":"post","link":"https:\/\/mihosoft.eu\/?p=827","title":{"rendered":"[Part 5] Custom Node Content"},"content":{"rendered":"<blockquote>\n  <p><strong>UPDATE [23.05.2014]:<\/strong> This tutorial need features that are no longer part of the JFXtras library. If you have problems building it, you might want to try the <a href=\"https:\/\/github.com\/miho\/JavaOne2013\">JavaOne2013<\/a> project that also makes use of the <em>Custom Node<\/em> feature.<\/p>\n<\/blockquote>\n\n<p>First of all, sorry for the delay. Many of you asked me how to add custom node content to VNodes. This tutorial will finally lift this secret \ud83d\ude09<\/p>\n\n<p>If you haven&#8217;t done so already please read the previous parts of the tutorial: <a href=\"https:\/\/mihosoft.eu\/?p=523\">Part 1<\/a>, <a href=\"https:\/\/mihosoft.eu\/?p=564\">Part 2<\/a>, <a href=\"https:\/\/mihosoft.eu\/?p=630\">Part 3<\/a> and <a href=\"https:\/\/mihosoft.eu\/?p=730\">Part 4<\/a>.<\/p>\n\n<h2>Short Demo on Custom Node Content:<\/h2>\n\n<iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"\/\/www.youtube.com\/embed\/ygGnIpKHSo4\" frameborder=\"0\" allowfullscreen><\/iframe>\n\n<h2>Adding Content<\/h2>\n\n<p>The latest snaphot release (<code>vworkflows-fx:0-1-r1-SNAPSHOT<\/code>) contains a new skin factory that allows to register node skins for specific value types. Instead of <code>FXValueSkinFactory<\/code>one has to use <code>FXValueSkinFactory<\/code>instead:<\/p>\n\n<pre><code> \/\/ create skin factory for flow visualization\n    FXValueSkinFactory fXSkinFactory = new FXValueSkinFactory(canvas.getContentPane());\n<\/code><\/pre>\n\n<p>Registering custom node skins is very easy:<\/p>\n\n<pre><code> \/\/ register visualizations for Integer, String and Image\n fXSkinFactory.addSkinClassForValueType(Integer.class, IntegerFlowNodeSkin.class);\n fXSkinFactory.addSkinClassForValueType(String.class, StringFlowNodeSkin.class);\n fXSkinFactory.addSkinClassForValueType(Image.class, ImageFlowNodeSkin.class);\n\n \/\/ generate the ui for the flow\n flow.addSkinFactories(fXSkinFactory);\n<\/code><\/pre>\n\n<p>It is also possible to register multiple factories at the same time that use a different set of node skins!<\/p>\n\n<h2>Anatomy of a Custom Flow Node Skin<\/h2>\n\n<p>A custom node skin usually inherits from <code>FXFlowNodeSkinBase<\/code> and provides a custom version of the <code>updateView()<\/code>method:<\/p>\n\n<pre><code>public class CustomFlowNodeSkin extends FXFlowNodeSkinBase {\n  @Override\n  public void updateView() {\n    \/\/ ...\n  }\n}\n<\/code><\/pre>\n\n<p>The version we provide in the tutorial project looks like this:<\/p>\n\n<pre><code>public abstract class CustomFlowNodeSkin extends FXFlowNodeSkinBase {\n\n    public CustomFlowNodeSkin(FXSkinFactory skinFactory,\n            VNode model, VFlow controller) {\n        super(skinFactory, model, controller);\n    }\n\n    protected abstract Node createView();\n\n    \/**\n     * Will be called once for each value change.\n     *\/\n    @Override\n    public void updateView() {\n\n        super.updateView();\n\n        \/\/ we don't create custom view for flows\n        if (getModel() instanceof VFlowModel) {\n            return;\n        }\n\n        \/\/ we don't create a custom view if no value has been defined\n        if (getModel().getValueObject().getValue() == null) {\n            return;\n        }\n\n        \/\/ create the view\n        Node view = createView();\n\n        \/\/ add the view to scalable content pane\n        if (view != null) {\n\n            ScalableContentPane scalableContentPane = new ScalableContentPane();\n            scalableContentPane.setPadding(new Insets(10));\n\n            GridPane nodePane = new GridPane();\n            nodePane.setAlignment(Pos.CENTER);\n            scalableContentPane.setContentPane(nodePane);\n\n            scalableContentPane.getContentPane().getChildren().add(view);\n            getNode().setContentPane(scalableContentPane);\n        }\n    }\n}\n<\/code><\/pre>\n\n<p>In the <code>updateView()<\/code> method we define a scalable container for the actual value view. If the node the skin visualizes does contain child nodes we don&#8217;t provide a custom view.<\/p>\n\n<h2>Example Skins:<\/h2>\n\n<p>The tutorial project provides the following skins:<\/p>\n\n<div class='photonic-flickr-stream photonic-stream ' id='photonic-flickr-stream-1'>\n\t<h3 class=\"photonic-single-photo-header photonic-single-flickr-photo-header\">Node Skin for String<\/h3>\n<a href=\"https:\/\/www.flickr.com\/photos\/99646668@N04\/9427826133\/\" title=\"Node Skin for String\"><img src=\"https:\/\/live.staticflickr.com\/7444\/9427826133_77cfc2c6b3_z.jpg\" alt=\"Node Skin for String\" loading=\"eager\" \/><\/a>\n<\/div><!-- .photonic-stream or .photonic-panel -->\n\n\n<div class='photonic-flickr-stream photonic-stream ' id='photonic-flickr-stream-2'>\n\t<h3 class=\"photonic-single-photo-header photonic-single-flickr-photo-header\">Node Skin for Integer<\/h3>\n<a href=\"https:\/\/www.flickr.com\/photos\/99646668@N04\/9430594766\/\" title=\"Node Skin for Integer\"><img src=\"https:\/\/live.staticflickr.com\/7455\/9430594766_3b794cf95c_z.jpg\" alt=\"Node Skin for Integer\" loading=\"eager\" \/><\/a>\n<\/div><!-- .photonic-stream or .photonic-panel -->\n\n\n<div class='photonic-flickr-stream photonic-stream ' id='photonic-flickr-stream-3'>\n\t<h3 class=\"photonic-single-photo-header photonic-single-flickr-photo-header\">Node Skin for Images<\/h3>\n<a href=\"https:\/\/www.flickr.com\/photos\/99646668@N04\/9430594816\/\" title=\"Node Skin for Images\"><img src=\"https:\/\/live.staticflickr.com\/7333\/9430594816_1c94391338_z.jpg\" alt=\"Node Skin for Images\" loading=\"eager\" \/><\/a>\n<\/div><!-- .photonic-stream or .photonic-panel -->\n\n\n<p>The LCD gauge and the segment display are part of the JFXtras library (<a href=\"http:\/\/jfxtras.org\">http:\/\/jfxtras.org<\/a>). See Gerrit Grunwalds <a href=\"https:\/\/github.com\/HanSolo\/Enzo\">Enzo<\/a> library for other very nice and lightweight gauges (depends on JDK 8).<\/p>\n\n<h1>Feedback<\/h1>\n\n<p>If you&#8217;d like to contribute or if you want to ask questions about <a href=\"http:\/\/vrl-studio.mihosoft.eu\">VRL<\/a>, <a href=\"https:\/\/github.com\/miho\/VWorkflows\">VWorkflows<\/a> and other related projects join the Developer &amp; User groups:<\/p>\n\n<ul>\n<li>Developer Group: <a href=\"https:\/\/groups.google.com\/forum\/#!forum\/vrl-developers\">https:\/\/groups.google.com\/forum\/#!forum\/vrl-developers<\/a><\/li>\n<li>User Group: <a href=\"https:\/\/groups.google.com\/forum\/#!forum\/vrl-studio-users\">https:\/\/groups.google.com\/forum\/#!forum\/vrl-studio-users<\/a><\/li>\n<\/ul>\n\n<h2>Demo Application<\/h2>\n\n<p>Download the <a href=\"https:\/\/github.com\/miho\/VWorkflows-Tutorial-05\">tutorial code<\/a> from GitHub.<\/p>\n\n<h3>Requirements:<\/h3>\n\n<ul>\n<li>Java 7 or a recent Java 8 preview build (>=b98)<\/li>\n<li>Optional: Netbeans >=7.3 with Gradle Plugin<\/li>\n<\/ul>\n\n<h3>Building &amp; Running:<\/h3>\n\n<p>To run the application from the command line just type <code>.\/gradlew run<\/code>.<\/p>\n\n<p>Stay tuned and <a href=\"https:\/\/twitter.com\/intent\/user?screen_name=mihosoft\">follow me on Twitter<\/a><\/p>\n\n<p><strong>To be continued&#8230;<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>UPDATE [23.05.2014]: This tutorial need features that are no longer part of the JFXtras library. If you have problems building it, you might want to try the JavaOne2013 project that also makes use of the Custom Node feature. First of all, sorry for the delay. Many of you asked me &#8230;<\/p>","protected":false},"author":1,"featured_media":846,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[129,21,5],"tags":[22,124,54,51,63,125,24,61,53,27,30,55,56],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/mihosoft.eu\/wp-content\/uploads\/2013\/08\/Bildschirmfoto-2013-08-03-um-18.53.29.png","jetpack_shortlink":"https:\/\/wp.me\/p2P2yR-dl","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mihosoft.eu\/index.php?rest_route=\/wp\/v2\/posts\/827"}],"collection":[{"href":"https:\/\/mihosoft.eu\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mihosoft.eu\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mihosoft.eu\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mihosoft.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=827"}],"version-history":[{"count":23,"href":"https:\/\/mihosoft.eu\/index.php?rest_route=\/wp\/v2\/posts\/827\/revisions"}],"predecessor-version":[{"id":1066,"href":"https:\/\/mihosoft.eu\/index.php?rest_route=\/wp\/v2\/posts\/827\/revisions\/1066"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mihosoft.eu\/index.php?rest_route=\/wp\/v2\/media\/846"}],"wp:attachment":[{"href":"https:\/\/mihosoft.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=827"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mihosoft.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=827"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mihosoft.eu\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=827"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}