<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>JavaFX News, Demos and Insight // FX Experience &#187; Controls</title>
	<atom:link href="http://fxexperience.com/category/controls/feed/" rel="self" type="application/rss+xml" />
	<link>http://fxexperience.com</link>
	<description>Sharing the Experience of JavaFX</description>
	<lastBuildDate>Fri, 03 Feb 2012 22:47:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>MoneyField</title>
		<link>http://fxexperience.com/2012/02/moneyfield/</link>
		<comments>http://fxexperience.com/2012/02/moneyfield/#comments</comments>
		<pubDate>Fri, 03 Feb 2012 22:47:40 +0000</pubDate>
		<dc:creator>Richard Bair</dc:creator>
				<category><![CDATA[Controls]]></category>
		<category><![CDATA[CSS]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=1675</guid>
		<description><![CDATA[I started writing an article about how to write new UI controls for OpenJFX using all the internal APIs and architecture and so forth. But then I discovered that the control I was writing as a proof of concept was not using any private API at all, and actually was implementing the Skin differently than [...]]]></description>
			<content:encoded><![CDATA[<p>I started writing an article about how to write new UI controls for OpenJFX using all the internal APIs and architecture and so forth. But then I discovered that the control I was writing as a proof of concept was not using any private API at all, and actually was implementing the Skin differently than I had imagined previously, and I thought I ought to blog about it. Behold, the MoneyField!</p>
<p><a href="http://fxexperience.com/wp-content/uploads/2012/02/MoneyField-Screenshot.png"><img src="http://fxexperience.com/wp-content/uploads/2012/02/MoneyField-Screenshot.png" alt="" title="MoneyField Screenshot" width="556" height="469" class="aligncenter size-full wp-image-1676" /></a></p>
<p><span id="more-1675"></span></p>
<p>The design space for a MoneyField is quite interesting. I did a bunch of research and tried out various different representations of &#8220;money&#8221; in Java, including <a href="http://joda-money.sourceforge.net/">Joda-Money</a>, but in the end the parsing was not satisfactory and I concluded to just use a good old fashioned BigDecimal to represent a monetary value. The best blog I found talking about the issues with BigDecimal for representing Money is this one titled <a href="http://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money/">BigDecimal and Your Money</a>. I think this same technique that I&#8217;m going to show works equally well whatever the data type is.</p>
<p>The most complex part of implementing this control was using the DecimalFormat for parsing the result back. In fact, I had a lot of trouble parsing the text in the MoneyField and producing a reliable BigDecimal out of it. I&#8217;m sure the implementation is lousy compared to what an expert in parsing such things could do, and I would welcome any feedback on my present implementation.</p>
<p>Writing the Control itself was trivial. I extended MoneyField from Control, and added a <code>value</code> property for holding the BigDecimal that would represent the monetary value of this control. I added a few other properties such as <code>editable</code>, <code>promptText</code>, and <code>prefColumnCount</code>. I also added an onAction event to MoneyField.</p>
<p>Now, there are some interesting things to talk about for you aspiring UI controls authors. Lets take a brief walk through the code.</p>
<pre class="brush: java; title: ; notranslate">
public class MoneyField extends Control {
    /**
     * Creates a new MoneyField. The style class is set to &quot;money-field&quot;.
     */
    public MoneyField() {
        getStyleClass().setAll(&quot;money-field&quot;);
    }

    @Override protected String getUserAgentStylesheet() {
        return getClass().getResource(&quot;MoneyField.css&quot;).toExternalForm();
    }
}
</pre>
<p>Every third party UI control must define the essential skeletal structure that you see in the above code snippet. You should provide a no-arg constructor (or it may not work in FXML). In your constructor, you should set the style class of the control to be the &#8220;css-ized&#8221; name of your control. Just toLowerCase and separate words by dashes. Then, because I want my skin to be entirely defined by CSS, I will override the getUserAgentStylesheet method and have it return a reference to a css file that I am going to define which acts as the &#8220;user agent stylesheet&#8221; for this control. That is, it provides the default CSS style for this control. Here is what my CSS file looks like:</p>
<pre class="brush: css; title: ; notranslate">
#money-text-field {
    -fx-skin: &quot;com.sun.javafx.scene.control.skin.TextFieldSkin&quot;;
}

.money-field {
    -fx-skin: &quot;com.fxexperience.javafx.scene.control.skin.MoneyFieldSkin&quot;;
    -fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;
    -fx-background-insets: 0, 1, 2;
    -fx-background-radius: 3, 2, 2;
    -fx-padding: 3 5 3 5;
    -fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
    -fx-cursor: text;
}

.money-field:focused {
    -fx-background-color: -fx-focus-color, -fx-text-box-border, -fx-control-inner-background;
    -fx-background-insets: -0.4, 1, 2;
    -fx-background-radius: 3.4, 2, 2;
}

.money-field:disabled {
    -fx-opacity: -fx-disabled-opacity;
}
</pre>
<p>There really isn&#8217;t a lot to say here, other than you can see I have a little trickery around the #money-text-field. Basically, I want the developer to be able to style the money field from CSS using .money-field, but I need to make sure the Skin used with the inner TextField is still a normal TextField. You&#8217;ll see how this fits together in a minute when I describe how the MoneyFieldSkin works.</p>
<p>Back in the MoneyField.java file, I also defined the above mentioned properties and events. First, here is what a &#8220;normal&#8221; property looks like. It is fully observable, readable, and writable.</p>
<pre class="brush: java; title: ; notranslate">
    /**
     * The value of the MoneyField. If null, the value will be treated as &quot;0&quot;, but
     * will still actually be null.
     */
    private ObjectProperty&lt;BigDecimal&gt; value = new SimpleObjectProperty&lt;BigDecimal&gt;(this, &quot;value&quot;);
    public final BigDecimal getValue() { return value.get(); }
    public final void setValue(BigDecimal value) { this.value.set(value); }
    public final ObjectProperty&lt;BigDecimal&gt; valueProperty() { return value; }
</pre>
<p>I am using the SimpleObjectProperty, which takes as parameters a reference to the bean that defined it, and the name of the property. The value of the <code>value</code> property defaults to null. Our doclet produces javadoc on the getter and setter without having to actually write it manually, so that&#8217;s why docs are missing for those. The editable property is likewise defined as a SimpleBooleanProperty.</p>
<p>The onAction is a very straightforward implementation. If you write your own controls and want to include your own action events, it is very easy to do:</p>
<pre class="brush: java; title: ; notranslate">
    /**
     * The action handler associated with this MoneyField, or
     * &lt;tt&gt;null&lt;/tt&gt; if no action handler is assigned.
     *
     * The action handler is normally called when the user types the ENTER key.
     */
    private ObjectProperty&lt;EventHandler&lt;ActionEvent&gt;&gt; onAction = new ObjectPropertyBase&lt;EventHandler&lt;ActionEvent&gt;&gt;() {
        @Override protected void invalidated() {
            setEventHandler(ActionEvent.ACTION, get());
        }

        @Override public Object getBean() { return MoneyField.this; }
        @Override public String getName() { return &quot;onAction&quot;; }
    };
    public final ObjectProperty&lt;EventHandler&lt;ActionEvent&gt;&gt; onActionProperty() { return onAction; }
    public final EventHandler&lt;ActionEvent&gt; getOnAction() { return onActionProperty().get(); }
    public final void setOnAction(EventHandler&lt;ActionEvent&gt; value) { onActionProperty().set(value); }
}
</pre>
<p>You&#8217;ll notice that in the invalidated method of the property I call setEventHandler. This method exists for the registration of such event handlers with the system. Another interesting thing here is that I&#8217;m not extending SimpleObjectProperty, but rather I&#8217;m extending ObjectPropertyBase.</p>
<p>Every property must implement the getBean() and getName() method. SimpleXXX properties have fields which hold the bean &#038; name, and you supply them in the constructor. But XXXPropertyBase properties have no such fields, and you must implement these methods. Why chose one over the other? Well, SimpleXXX properties will have smaller static footprint because they are using a class instead of defining a class, but will have larger dynamic footprint because they have two additional fields which must be saved for each instance. XXXPropertyBase uses larger static footprint because it defines a class, but smaller dynamic footprint because it doesn&#8217;t have to save these two fields. Since I had to subclass an object property anyway for the sake of implementing the invalidated method, I might as well extend ObjectPropertyBase and save a couple fields at runtime.</p>
<p>The Skin for MoneyField was quite interesting. At first I thought I would extend from TextFieldSkin (an internal implementation in JavaFX) but then discovered this wasn&#8217;t necessary. Instead, I could just implement the Skin interface and return a TextField as the &#8220;node&#8221; of the Skin. So the MoneyField defines my UI, and the TextField is actually what I end up using to represent the MoneyField. And then there is a pile of glue code which ties the TextField to the MoneyField. The other nice thing about this approach was that I could implement the entirety of MoneyField and MoneyFieldSkin without using any private API (which, when I started writing this, I didn&#8217;t think was possible).</p>
<pre class="brush: java; title: ; notranslate">
public class MoneyFieldSkin implements Skin&lt;MoneyField&gt; {
    /**
     * The {@code Control} that is referencing this Skin. There is a
     * one-to-one relationship between a {@code Skin} and a {@code Control}.
     * When a {@code Skin} is set on a {@code Control}, this variable is
     * automatically updated.
     */
    private MoneyField control;

    /**
     * This textField is used to represent the MoneyField.
     */
    private TextField textField;

    /**
     * Create a new MoneyFieldSkin.
     * @param control The MoneyField
     */
    public MoneyFieldSkin(final MoneyField control) {
        this.control = control;

        // Create the TextField that we are going to use to represent this MoneyFieldSkin.
        // The textField restricts input so that only valid digits that contribute to the
        // Money can be input.
        textField = new TextField() { // ...
        };

        // ... and a lot more stuff!
    }

    @Override public MoneyField getSkinnable() {
        return control;
    }

    @Override public Node getNode() {
        return textField;
    }

    /**
     * Called by a Skinnable when the Skin is replaced on the Skinnable. This method
     * allows a Skin to implement any logic necessary to clean up itself after
     * the Skin is no longer needed. It may be used to release native resources.
     * The methods {@link #getSkinnable()} and {@link #getNode()}
     * should return null following a call to dispose. Calling dispose twice
     * has no effect.
     */
    @Override public void dispose() {
        textField = null;
    }

    // ... and a lot more stuff!
}
</pre>
<p>The above code lays out the very basic skeletal structure of a Skin. This skin implements the Skin interface directly. The constructor takes the MoneyField it will be working with, and creates the TextField which will represent the skin (or &#8220;draw&#8221; the skin). In the dispose method, we throw away the TextField.</p>
<p>If you ran the code above, you would find a MoneyField that looked and felt like a TextField. None of the properties on the MoneyField would work, but from a visual perspective and a text entry perspective you&#8217;d have, essentially, a TextField. The rest of MoneyFieldSkin is devoted to wiring up the MoneyField API to the TextField and implementing all the parsing and formatting necessary to make it look like a money field.</p>
<p>The remainder of the MoneyFieldSkin isn&#8217;t worth going through in this blog post, though you can <a href='http://fxexperience.com/wp-content/uploads/2012/02/MoneyField.zip'>download the Intellij project</a> and check it out for yourself.</p>
<p>When Jonathan tried this out last night he was getting exceptions with the New Zealand currency. I suspect there may be problems in Australia too. I&#8217;m hoping one of our readers from down under will give it a look and see where my bug is <img src='http://fxexperience.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> .</p>
<p>Using the MoneyField is really very straightforward. I hope it is actually useful to boot. I&#8217;m wondering if anybody industrious out there might take this design strategy and write an EmailField and UrlField control, maybe a NumberField control too! It seems eminently doable using only the public API and would make a great addition to the ecosystem (as well as a great addition to the toolkit!).</p>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2012/02/moneyfield/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Fun JavaFX 2.0 Audio Player</title>
		<link>http://fxexperience.com/2012/01/fun-javafx-2-0-audio-player/</link>
		<comments>http://fxexperience.com/2012/01/fun-javafx-2-0-audio-player/#comments</comments>
		<pubDate>Sat, 07 Jan 2012 08:10:11 +0000</pubDate>
		<dc:creator>Jasper Potts</dc:creator>
				<category><![CDATA[Controls]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Demo]]></category>
		<category><![CDATA[UI Design]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=1586</guid>
		<description><![CDATA[I was inspired by Dean Iverson&#8217;s tweet with a audio equalizer in JavaFX: #JavaFX rocks. Literally. An example from our upcoming Pro JavaFX 2 book: pic.twitter.com/tSI4Vry4 and the equalizer view from that Pro JavaFX 2 example app: pic.twitter.com/T6jxvrf9 . updated pic.twitter.com/FqzgVimG So wanted to have a go at doing one my self, so little while later [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://fxexperience.com/wp-content/uploads/2012/01/FXExperiencePlayer-BG.png"><img class="aligncenter size-medium wp-image-1587" title="FXExperiencePlayer" src="http://fxexperience.com/wp-content/uploads/2012/01/FXExperiencePlayer-BG-300x190.png" alt="" width="300" height="190" /></a></p>
<p>I was inspired by <a href="http://twitter.com/deanriverson">Dean Iverson&#8217;s</a> tweet with a audio equalizer in JavaFX:</p>
<blockquote><p>#JavaFX rocks. Literally. An example from our upcoming Pro JavaFX 2 book: <a href="http://pic.twitter.com/tSI4Vry4">pic.twitter.com/tSI4Vry4</a><br />
and the equalizer view from that Pro JavaFX 2 example app: <a href="http://pic.twitter.com/T6jxvrf9">pic.twitter.com/T6jxvrf9</a><br />
. updated <a href="http://pic.twitter.com/FqzgVimG">pic.twitter.com/FqzgVimG</a></p></blockquote>
<p>So wanted to have a go at doing one my self, so little while later I have a design and built a working application. Demo video after the break.</p>
<p><span id="more-1586"></span></p>
<p><iframe src="http://www.youtube.com/embed/QhFXd3Ost3w?rel=0&amp;hd=1" frameborder="0" width="960" height="720"></iframe></p>
<h3>The Design</h3>
<p>I started the design with a free Photoshop file from Diego Monzon:</p>
<p><a href="http://dribbble.com/shots/146460-UI-Rack-Pro-7-Freebie"><img class="alignnone" title="Starting Free PSD" src="http://dribbble.com/system/users/11040/screenshots/146460/ui7.jpg?1309482012" alt="" width="400" height="300" /></a></p>
<p>You will see that some of it like the cool VU meters made it direct though to my design but then I played around with the other parts. I have added a link to the Photoshop file I created so you can see what I have done if you are intrested. From the photoshop file I produced a few PNG files which are used in the application, you can see them in the src images directory. I found a free LCD dot matrix font from dafont which is what I am using for the text on the screen. You can see in the code how this is loaded in JavaFX.</p>
<p><a title="Photoshop design" href="http://fxexperience.com/applications/FXExperiencePlayer-BG.psd.zip">Download the Photoshop File</a> 18Mb</p>
<h4>The Code</h4>
<p>The application did not take that long to build, guess about a day in total and about 800 lines of code. I think it is fully working other than the balance is a little fiddly with the mouse. All the control customizations should have full keyboard navigation <img src='http://fxexperience.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> . It is mostly standard UI Controls just styled with Images and CSS. The round knobs like balance and volume I had to do something special for and created a knob skin for the Slider control. There was a bunch of the standard logic to load and parse M3U and XML playlists, a very simple data model object PlayList which just contains a url which it was loaded from and a observable list of Pairs, each Pair being a name for the song and a url to the song. We did not have all the low level media API I needed for the VU meters but as they are only really for show I made of some data from what we can get from AudioSpectrumListener.</p>
<p><a title="Source and NB Project" href="http://fxexperience.com/applications/FxExperiencePlayer.zip">Download the source and Netbeans Project</a> 1.5Mb</p>
<p>Hope you enjoy playing with this fun little app as much as I did creating it. Hope it goes to show that you can create pretty much any UI that you can imagine in JavaFX 2.0 and even use standard controls so you maintain keyboard navigation, accessibility etc.</p>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2012/01/fun-javafx-2-0-audio-player/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Styling FX Buttons with CSS</title>
		<link>http://fxexperience.com/2011/12/styling-fx-buttons-with-css/</link>
		<comments>http://fxexperience.com/2011/12/styling-fx-buttons-with-css/#comments</comments>
		<pubDate>Tue, 20 Dec 2011 23:36:03 +0000</pubDate>
		<dc:creator>Jasper Potts</dc:creator>
				<category><![CDATA[Controls]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Tips n' Tricks]]></category>
		<category><![CDATA[UI Design]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=1523</guid>
		<description><![CDATA[A number of people have asked me recently can I create this look or that look using CSS in JavaFX. Or they have said that you could never do that! So I thought I would do a little experiment and try recreating a bunch of common button styles purely using CSS. So without further ado, [...]]]></description>
			<content:encoded><![CDATA[<p>A number of people have asked me recently can I create this look or that look using CSS in JavaFX. Or they have said that you could never do that! So I thought I would do a little experiment and try recreating a bunch of common button styles purely using CSS. So without further ado, here is the result:</p>
<p><img class="aligncenter size-full wp-image-1524" title="CSS Styled Buttons" src="http://fxexperience.com/wp-content/uploads/2011/12/Screen-Shot-2011-12-20-at-2.51.18-PM.png" alt="" width="706" height="873" /><br />
<span id="more-1523"></span><br />
All of these are mostly created with multiple background fill layers each with a gradient. Then there is a little font tweaking and some subtle effects. So for example all you need to make a JavaFX button look like a Windows 7 button is:</p>
<pre class="brush: css; title: ; notranslate">
#windows7-default {
    -fx-background-color:
        #3c7fb1,
        linear-gradient(#fafdfe, #e8f5fc),
        linear-gradient(#eaf6fd 0%, #d9f0fc 49%, #bee6fd 50%, #a7d9f5 100%);
    -fx-background-insets: 0,1,2;
    -fx-background-radius: 3,2,1;
    -fx-padding: 3 30 3 30;
    -fx-text-fill: black;
    -fx-font-size: 14px;
}
</pre>
<p>The first line defines the 3 background fills, first is solid color and the other two are linear gradients. The background-insets offsets the backgrounds so they do not 100% paint over each other and the second background is 1px in from the outside and the 3rd background is 2px in from the outside of the button. The background-radius us setting the corner radius&#8217;s of the 3 backgrounds getting smaller as the backgrounds move in, this makes the gap between the borders a consistent 1 pixel all the way around. Padding adds extra space around the text to make the button bigger by default. Then the last two lines set the text color and size. That is all there is too it.</p>
<p>I have not included styles for the pressed, over and focused states of all the buttons but they can all be easily added in a similar way. I have included a over and pressed state for the &#8220;Record Sales&#8221; button as a example. </p>
<p>You can download the complete Netbeans project with all the code here.<br />
<b><a href="/applications/ButtonStyles.zip">ButtonStyles.zip</a></b></p>
<p>Here is the CSS code of all the button styles you see above:</p>
<pre class="brush: css; title: ; notranslate">
#green {
    -fx-background-color:
        linear-gradient(#f0ff35, #a9ff00),
        radial-gradient(center 50% -40%, radius 200%, #b8ee36 45%, #80c800 50%);
    -fx-background-radius: 6, 5;
    -fx-background-insets: 0, 1;
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.4) , 5, 0.0 , 0 , 1 );
    -fx-text-fill: #395306;
}
#round-red {
    -fx-background-color: linear-gradient(#ff5400, #be1d00);
    -fx-background-radius: 30;
    -fx-background-insets: 0;
    -fx-text-fill: white;
}
#bevel-grey {
    -fx-background-color:
        linear-gradient(#f2f2f2, #d6d6d6),
        linear-gradient(#fcfcfc 0%, #d9d9d9 20%, #d6d6d6 100%),
        linear-gradient(#dddddd 0%, #f6f6f6 50%);
    -fx-background-radius: 8,7,6;
    -fx-background-insets: 0,1,2;
    -fx-text-fill: black;
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 );
}
#glass-grey {
    -fx-background-color:
        #c3c4c4,
        linear-gradient(#d6d6d6 50%, white 100%),
        radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%, rgba(230,230,230,0) 50%);
    -fx-background-radius: 30;
    -fx-background-insets: 0,1,1;
    -fx-text-fill: black;
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 3, 0.0 , 0 , 1 );
}
#shiny-orange {
    -fx-background-color:
        linear-gradient(#ffd65b, #e68400),
        linear-gradient(#ffef84, #f2ba44),
        linear-gradient(#ffea6a, #efaa22),
        linear-gradient(#ffe657 0%, #f8c202 50%, #eea10b 100%),
        linear-gradient(from 0% 0% to 15% 50%, rgba(255,255,255,0.9), rgba(255,255,255,0));
    -fx-background-radius: 30;
    -fx-background-insets: 0,1,2,3,0;
    -fx-text-fill: #654b00;
    -fx-font-weight: bold;
    -fx-font-size: 14px;
    -fx-padding: 10 20 10 20;
}
#dark-blue {
    -fx-background-color:
        #090a0c,
        linear-gradient(#38424b 0%, #1f2429 20%, #191d22 100%),
        linear-gradient(#20262b, #191d22),
        radial-gradient(center 50% 0%, radius 100%, rgba(114,131,148,0.9), rgba(255,255,255,0));
    -fx-background-radius: 5,4,3,5;
    -fx-background-insets: 0,1,2,0;
    -fx-text-fill: white;
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 );
    -fx-font-family: &quot;Arial&quot;;
    -fx-text-fill: linear-gradient(white, #d0d0d0);
    -fx-font-size: 12px;
    -fx-padding: 10 20 10 20;
}
#dark-blue Text {
    -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.9) , 1, 0.0 , 0 , 1 );
}
#record-sales {
    -fx-padding: 8 15 15 15;
    -fx-background-insets: 0,0 0 5 0, 0 0 6 0, 0 0 7 0;
    -fx-background-radius: 8;
    -fx-background-color:
        linear-gradient(from 0% 93% to 0% 100%, #a34313 0%, #903b12 100%),
        #9d4024,
        #d86e3a,
        radial-gradient(center 50% 50%, radius 100%, #d86e3a, #c54e2c);
    -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.75) , 4,0,0,1 );
    -fx-font-weight: bold;
    -fx-font-size: 1.1em;
}
#record-sales:hover {
    -fx-background-color:
        linear-gradient(from 0% 93% to 0% 100%, #a34313 0%, #903b12 100%),
        #9d4024,
        #d86e3a,
        radial-gradient(center 50% 50%, radius 100%, #ea7f4b, #c54e2c);
}
#record-sales:pressed {
    -fx-padding: 10 15 13 15;
    -fx-background-insets: 2 0 0 0,2 0 3 0, 2 0 4 0, 2 0 5 0;
}
#record-sales Text {
    -fx-fill: white;
    -fx-effect: dropshadow( gaussian , #a30000 , 0,0,0,2 );
}
#rich-blue {
    -fx-background-color:
        #000000,
        linear-gradient(#7ebcea, #2f4b8f),
        linear-gradient(#426ab7, #263e75),
        linear-gradient(#395cab, #223768);
    -fx-background-insets: 0,1,2,3;
    -fx-background-radius: 3,2,2,2;
    -fx-padding: 12 30 12 30;
    -fx-text-fill: white;
    -fx-font-size: 12px;
}
#rich-blue Text {
    -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.8) , 0, 0.0 , 0 , 1);
}
#big-yellow {
    -fx-background-color:
        #ecebe9,
        rgba(0,0,0,0.05),
        linear-gradient(#dcca8a, #c7a740),
        linear-gradient(#f9f2d6 0%, #f4e5bc 20%, #e6c75d 80%, #e2c045 100%),
        linear-gradient(#f6ebbe, #e6c34d);
    -fx-background-insets: 0,9 9 8 9,9,10,11;
    -fx-background-radius: 50;
    -fx-padding: 15 30 15 30;
    -fx-font-family: &quot;Helvetica&quot;;
    -fx-font-size: 18px;
    -fx-text-fill: #311c09;
    -fx-effect: innershadow( three-pass-box , rgba(0,0,0,0.1) , 2, 0.0 , 0 , 1);
}
#big-yellow Text {
    -fx-effect: dropshadow( one-pass-box , rgba(255,255,255,0.5) , 0, 0.0 , 0 , 1);
}
#iphone-toolbar {
    -fx-background-color: linear-gradient(#98a8bd 0%, #8195af 25%, #6d86a4 100%);
}
#iphone {
    -fx-background-color:
        #a6b5c9,
        linear-gradient(#303842 0%, #3e5577 20%, #375074 100%),
        linear-gradient(#768aa5 0%, #849cbb 5%, #5877a2 50%, #486a9a 51%, #4a6c9b 100%);
    -fx-background-insets: 0 0 -1 0,0,1;
    -fx-background-radius: 5,5,4;
    -fx-padding: 7 30 7 30;
    -fx-text-fill: #242d35;
    -fx-font-family: &quot;Helvetica&quot;;
    -fx-font-size: 12px;
    -fx-text-fill: white;
}
#iphone Text {
    -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.8) , 0, 0.0 , 0 , -1 );
}
#ipad-dark-grey {
    -fx-background-color:
        linear-gradient(#686868 0%, #232723 25%, #373837 75%, #757575 100%),
        linear-gradient(#020b02, #3a3a3a),
        linear-gradient(#9d9e9d 0%, #6b6a6b 20%, #343534 80%, #242424 100%),
        linear-gradient(#8a8a8a 0%, #6b6a6b 20%, #343534 80%, #262626 100%),
        linear-gradient(#777777 0%, #606060 50%, #505250 51%, #2a2b2a 100%);
    -fx-background-insets: 0,1,4,5,6;
    -fx-background-radius: 9,8,5,4,3;
    -fx-padding: 15 30 15 30;
    -fx-font-family: &quot;Helvetica&quot;;
    -fx-font-size: 18px;
    -fx-font-weight: bold;
    -fx-text-fill: white;
    -fx-effect: dropshadow( three-pass-box , rgba(255,255,255,0.2) , 1, 0.0 , 0 , 1);
}
#ipad-dark-grey Text {
    -fx-effect: dropshadow( one-pass-box , black , 0, 0.0 , 0 , -1 );
}
#ipad-grey {
    -fx-background-color:
        linear-gradient(#686868 0%, #232723 25%, #373837 75%, #757575 100%),
        linear-gradient(#020b02, #3a3a3a),
        linear-gradient(#b9b9b9 0%, #c2c2c2 20%, #afafaf 80%, #c8c8c8 100%),
        linear-gradient(#f5f5f5 0%, #dbdbdb 50%, #cacaca 51%, #d7d7d7 100%);
    -fx-background-insets: 0,1,4,5;
    -fx-background-radius: 9,8,5,4;
    -fx-padding: 15 30 15 30;
    -fx-font-family: &quot;Helvetica&quot;;
    -fx-font-size: 18px;
    -fx-font-weight: bold;
    -fx-text-fill: #333333;
    -fx-effect: dropshadow( three-pass-box , rgba(255,255,255,0.2) , 1, 0.0 , 0 , 1);
}
#ipad-grey Text {
    -fx-effect: dropshadow( one-pass-box , white , 0, 0.0 , 0 , 1 );
}
#lion-default {
    -fx-background-color:
        rgba(0,0,0,0.08),
        linear-gradient(#5a61af, #51536d),
        linear-gradient(#e4fbff 0%,#cee6fb 10%, #a5d3fb 50%, #88c6fb 51%, #d5faff 100%);
    -fx-background-insets: 0 0 -1 0,0,1;
    -fx-background-radius: 5,5,4;
    -fx-padding: 3 30 3 30;
    -fx-text-fill: #242d35;
    -fx-font-size: 14px;
}
#lion {
    -fx-background-color:
        rgba(0,0,0,0.08),
        linear-gradient(#9a9a9a, #909090),
        linear-gradient(white 0%, #f3f3f3 50%, #ececec 51%, #f2f2f2 100%);
    -fx-background-insets: 0 0 -1 0,0,1;
    -fx-background-radius: 5,5,4;
    -fx-padding: 3 30 3 30;
    -fx-text-fill: #242d35;
    -fx-font-size: 14px;
}
#windows7-default {
    -fx-background-color:
        #3c7fb1,
        linear-gradient(#fafdfe, #e8f5fc),
        linear-gradient(#eaf6fd 0%, #d9f0fc 49%, #bee6fd 50%, #a7d9f5 100%);
    -fx-background-insets: 0,1,2;
    -fx-background-radius: 3,2,1;
    -fx-padding: 3 30 3 30;
    -fx-text-fill: black;
    -fx-font-size: 14px;
}
#windows7 {
    -fx-background-color:
        #707070,
        linear-gradient(#fcfcfc, #f3f3f3),
        linear-gradient(#f2f2f2 0%, #ebebeb 49%, #dddddd 50%, #cfcfcf 100%);
    -fx-background-insets: 0,1,2;
    -fx-background-radius: 3,2,1;
    -fx-padding: 3 30 3 30;
    -fx-text-fill: black;
    -fx-font-size: 14px;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2011/12/styling-fx-buttons-with-css/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Alternate row highlighting in empty TableView (and ListView) rows</title>
		<link>http://fxexperience.com/2011/11/alternate-row-highlighting-in-empty-tableview-and-listview-rows/</link>
		<comments>http://fxexperience.com/2011/11/alternate-row-highlighting-in-empty-tableview-and-listview-rows/#comments</comments>
		<pubDate>Mon, 28 Nov 2011 04:53:05 +0000</pubDate>
		<dc:creator>Jonathan Giles</dc:creator>
				<category><![CDATA[Controls]]></category>
		<category><![CDATA[Tips n' Tricks]]></category>
		<category><![CDATA[UI Design]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=1499</guid>
		<description><![CDATA[One thing I see asked a lot (either directly via email, or in the rather excellent JavaFX Forums) is why does TableView (and ListView) look like this in JavaFX 2.0: Or, more specifically, why it doesn&#8217;t look like this: I can see where this question comes from, given this is how Swing&#8217;s JTable looks: It [...]]]></description>
			<content:encoded><![CDATA[<p>One thing I see asked a lot (either directly via email, or in the rather excellent <a href="https://forums.oracle.com/forums/forum.jspa?forumID=1385&amp;start=0">JavaFX Forums</a>) is why does TableView (and ListView) look like this in JavaFX 2.0:</p>
<div id="attachment_1500" class="wp-caption aligncenter" style="width: 509px"><img class="size-full wp-image-1500" title="blog-table-1" src="http://fxexperience.com/wp-content/uploads/2011/11/blog-table-1.png" alt="" width="499" height="310" /><p class="wp-caption-text">A TableView control using default styling and API as provided in JavaFX 2.0.</p></div>
<p><span id="more-1499"></span>Or, more specifically, why it doesn&#8217;t look like this:</p>
<div id="attachment_1502" class="wp-caption aligncenter" style="width: 508px"><img class="size-full wp-image-1502" title="blog-table-2" src="http://fxexperience.com/wp-content/uploads/2011/11/blog-table-2.png" alt="" width="498" height="307" /><p class="wp-caption-text">A TableView without alternate row highlighting in empty rows</p></div>
<p>I can see where this question comes from, given this is how Swing&#8217;s JTable looks:</p>
<div id="attachment_1501" class="wp-caption aligncenter" style="width: 535px"><img class="size-full wp-image-1501" title="blog-table-3" src="http://fxexperience.com/wp-content/uploads/2011/11/blog-table-3.png" alt="" width="525" height="188" /><p class="wp-caption-text">Swing JTable</p></div>
<p>It comes down to taste I guess, but given that there is a subset of developers who expect this look, I thought I&#8217;d put a blog post up here to clarify how it is done (and to save me having to explain it to people individually by email!) <img src='http://fxexperience.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  All you need is the following CSS for TableView:</p>
<pre class="brush: css; title: ; notranslate">
.table-row-cell:empty {
    -fx-background-color: white;
}

.table-row-cell:empty .table-cell {
    -fx-border-width: 0px;
}
</pre>
<p>Similarly, for ListView, you can get away with just the first statement above (slightly modified for ListView):</p>
<pre class="brush: css; title: ; notranslate">
.list-cell:empty {
    -fx-background-color: white;
}
</pre>
<p>The end result is precisely what you see in the second image above (there ain&#8217;t no photoshop trickery here!) <img src='http://fxexperience.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Hopefully this helps people customising their user interfaces, and hopefully also encourages you to further explore what is possible. The best starting point is the excellent <a href="http://docs.oracle.com/javafx/">JavaFX documentation website</a>, the <a href="http://docs.oracle.com/javafx/2.0/api/javafx/scene/doc-files/cssref.html">CSS Reference</a> (which is about to be updated for 2.0.2 I believe), and as always, the caspian.css file that ships with every JavaFX release (embedded within jfxrt.jar).</p>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2011/11/alternate-row-highlighting-in-empty-tableview-and-listview-rows/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Introduction to DataFX</title>
		<link>http://fxexperience.com/2011/10/introduction-to-datafx/</link>
		<comments>http://fxexperience.com/2011/10/introduction-to-datafx/#comments</comments>
		<pubDate>Mon, 24 Oct 2011 02:31:34 +0000</pubDate>
		<dc:creator>Jonathan Giles</dc:creator>
				<category><![CDATA[Controls]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Links]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=1452</guid>
		<description><![CDATA[One of the projects I worked on leading up to JavaOne 2011 was the DataFX project, which, as I wrote on the website, &#8220;is an open source project that intends to make retrieving, massaging, populating, viewing, and editing data in JavaFX UI controls easier. It’s all that boring kludge work you have to do between [...]]]></description>
			<content:encoded><![CDATA[<p>One of the projects I worked on leading up to JavaOne 2011 was the <a href="http://www.javafxdata.org">DataFX project</a>, which, as I wrote on the website, &#8220;is an open source project that intends to make retrieving, massaging, populating, viewing, and editing data in JavaFX UI controls easier. It’s all that boring kludge work you have to do between getting user requirements and delivering a rich user experience.&#8221;</p>
<p>DataFX is a project Johan Vos and I have been working on for many months now, and it has gone through a number of iterations in that time. At JavaOne 2011 we put out a first release (let&#8217;s call it version 0.0.1 for lack of an official version number), and today I want to briefly introduce it for those of you who didn&#8217;t attend JavaOne. However, even if you didn&#8217;t attend JavaOne, we&#8217;ve put the <a href="http://www.javafxdata.org/downloads/javaone-2011-slides.pdf">slides online</a>.</p>
<p>To be very clear, DataFX is not an Oracle project! Johan and I both developed this in our own time, and it does not necessarily represent the future plans of the official JavaFX project. This project was built to make many of the UI controls I develop easier to work with by filling in the gaps as of the current JavaFX 2.0 release. Oracle may or may not have future plans in the same area as DataFX, but for now DataFX exists to fill the gap. For more details, check out the <a href="http://www.javafxdata.org/faq/">DataFX FAQ</a>.</p>
<p>To make things easier to understand let&#8217;s conceptually split DataFX into two sub-projects which attack a common problem from two different angles.</p>
<p><span id="more-1452"></span><strong>Data Sources</strong></p>
<p>The first sub-project deals with the logistics of getting data into the user interface controls by providing various <a title="Data Sources" href="http://www.javafxdata.org/content/data-sources/">data source adapters</a>. These adapters are intended to provide convenience around populating JavaFX controls such as ListView and TableView (at this stage, although we&#8217;d love to eventually also support populating TreeView and charts). This is achieved by abstracting away the implementation details surrounding data source retrieval and massaging, such that data can be rapidly loaded and seen on screen. This is done by essentially creating data sources that are ObservableList instances, allowing for them to be directly loaded into the ListView and TableView &#8216;items&#8217; property. In the case of TableView, DataFX can also auto-generate TableColumn instances with pre-built cell value factories defined.</p>
<pre class="brush: java; title: ; notranslate">
DataSourceReader dsr1 = new FileReader(&quot;foo.xml&quot;);
DataSourceReader dsr2 = new NetworkReader (&quot;http://foo.bar/foo.xml&quot;);

XmlDataSource ds1 = new XmlDataSource(dsr1);

TableView tableView = new TableView();
tableView.setItems(ds1);
tableView.getColumns().addAll(ds1.getColumns());
</pre>
<p>An example of how to use this API is shown above. We define two DataSourceReader instances: one is a FileReader, the other a NetworkReader (although in this example the NetworkReader isn&#8217;t used any further). In both cases it is important to note that the DataSourceReader is datatype-agnostic &#8211; that is, it only cares about where the data is coming from, not the format of the data. At present DataFX only ships with these two DataSourceReader implementations, but it is probable that over time we will add more classes. You can see the JavaDoc for these classes in the <a href="http://www.javafxdata.org/javadoc/index.html?org/javafxdata/datasources/io/package-summary.html">org.javafxdata.datasources.io</a> package.</p>
<p>Once the DataSourceReader instance is created, we must then create a DataSource using one of the implementations provided in the <a href="http://www.javafxdata.org/javadoc/index.html?org/javafxdata/datasources/protocol/package-summary.html">org.javafxdata.datasources.protocol</a> package. In the example below, we create an XmlDataSource, which knows how to parse an XML data stream and create an ObservableList that can be set directly into a ListView or TableView. Also, because it extends <a href="http://www.javafxdata.org/javadoc/index.html?org/javafxdata/datasources/protocol/TableWrapper.html">TableWrapper</a>, it has a getColumns() method that can be used to set the columns in a TableView instance. At present, we have DataSource implementations for CSV files, Java Objects, <a href="http://www.redfx.org">RedFX</a> and XML files. Again, we intend to add further data source implementations in the future as time permits.</p>
<p>Now that the DataSource exists, we create a TableView instance, and set the items property to be the data source itself. We also add all the columns the data source generates into the TableView columns property. This not only creates a column header with default text, it also creates a default cell value factory to populate the cells in that column. This is often the most complex part of using a TableView, and is completely unnecessary in data sources by default (of course, you are free to just create your own TableColumn instances also).</p>
<p>With a (by default) version number of 0.0.1, we recognise that DataFX is not a finished product. For one, it never will be &#8211; it&#8217;s a free project that we are making available here &#8211; but also we recognise the gaping holes in the data sources sub-project. In particular, along with what I&#8217;ve listed above, we also want to provide much more convenience around features such as sorting, filtering, and on-demand loading. If there is anything else that you think is missing, you can find the contact details over at the <a href="http://www.javafxdata.org">DataFX website</a>.</p>
<p><strong>Cell Factories</strong></p>
<div class="wp-caption alignright" style="width: 401px"><img title="CheckBox TreeView" src="http://www.javafxdata.org/screenshots/CheckBoxCell-TreeView.PNG" alt="" width="391" height="519" /><p class="wp-caption-text">A TreeView with support for CheckBoxes</p></div>
<p>The other sub-project of the DataFX project is dealing with the need to more easily visualise data once it is already appearing inside the ListView, TreeView and TableView controls. This customisation is done almost entirely using the cell factories API that is available in all three controls (I&#8217;ll try to blog more about cell factories in a future post). In other words, the default cell factory implementation provided by ListView/TreeView/TableView is sometimes not sufficient &#8211; it simply calls toString() on the data objects and shows it onscreen. In these circumstances, it is necessary to provide your own cell factory, and this is where DataFX comes in &#8211; it provides a number of useful cell factories that you can drop in and use, often with only one line of code.</p>
<p>There are a number of pre-built custom cells. You can see the full list on the <a href="http://www.javafxdata.org/content/cell-factories/">cell factories page</a> on the DataFX website, but briefly, it includes the following:</p>
<ul>
<li>TextField (for text editing)</li>
<li>ChoiceBox (for selecting between a small number of choices)</li>
<li>CheckBox (for toggling a boolean property)</li>
<li>ProgressBar (for graphically representing a number as it progresses from 0.0 to 1.0)</li>
<li>Money formatting (formats a Number instance using a default Locale, and applies CSS to make negative values red, and positive values green)</li>
<li>Dynamically variable row heights (e.g. on mouse hover, a row will grow to show more information)</li>
</ul>
<p>For each cell type mentioned above, there is a separate static factory class that contains a number of convenience methods. These methods should be your first port of call. You can find both the cells, and the more convenient cell factories API in the <a href="http://www.javafxdata.org/javadoc/index.html?org/javafxdata/control/cell/package-summary.html">org.javafxdata.control.cell</a> package. You should explore these javadocs as I have spent considerable effort ensuring that they contain all necessary information. For screenshots of all the cell factories currently available, you can go to the <a href="http://www.javafxdata.org/content/cell-factories/">cell factories page</a> on the DataFX website (clicking on the ticks will take you to a screenshot for that feature &#8211; I&#8217;ll try to make this more intuitive when I have time). I&#8217;ve attached one screenshot to this blog post, for the CheckBox TreeView.</p>
<p>In the code snippet below, I demonstrate how simple it is to make a ListView support text editing of cells when the user double-clicks on a row. In particular, note that the last line is the only line that uses the DataFX API (i.e. the reference to TextFieldCellFactory is from DataFX) &#8211; the other three lines are normal ListView API that should already be familiar to many developers.</p>
<pre class="brush: java; title: ; notranslate">
ListView&lt;String&gt; listView = new ListView&lt;String&gt;();
listView.setItems(namesList);
listView.setEditable(true);
listView.setCellFactory(TextFieldCellFactory.forListView());
</pre>
<p>Again, the cell factories implementation certainly lacks features, but we think it provides a good starting point for the most common use cases. If there is a cell factory you think is missing, get in touch and let us know &#8211; we may just build it for you! <img src='http://fxexperience.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p><strong>Conclusion</strong></p>
<p>Johan and I both hope that DataFX is useful to you. We welcome feedback and requests for features &#8211; our goal is to make development of rich user interfaces in JavaFX 2.0 as simple as possible. Any use cases you can share with us will be much appreciated as we continue to refine our APIs, as well as provide additional data sources and cell factory implementations.</p>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2011/10/introduction-to-datafx/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>SplitPane in JavaFX 2.0</title>
		<link>http://fxexperience.com/2011/06/splitpane-in-javafx-2-0/</link>
		<comments>http://fxexperience.com/2011/06/splitpane-in-javafx-2-0/#comments</comments>
		<pubDate>Sun, 05 Jun 2011 06:24:15 +0000</pubDate>
		<dc:creator>Jonathan Giles</dc:creator>
				<category><![CDATA[Controls]]></category>
		<category><![CDATA[Tips n' Tricks]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=1242</guid>
		<description><![CDATA[We&#8217;ve introduced a SplitPane control in JavaFX 2.0, and today I thought I&#8217;d point out an interesting subtlety in the API. For the longest time our SplitPane API primarily consisted of the normal &#8216;left&#8217; and &#8216;right&#8217; (or &#8216;top&#8217; and &#8216;bottom&#8217;) properties (indeed, the JavaDoc as of today still refers to this API). These were synonomous [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve introduced a SplitPane control in JavaFX 2.0, and today I thought I&#8217;d point out an interesting subtlety in the API. For the longest time our SplitPane API primarily consisted of the normal &#8216;left&#8217; and &#8216;right&#8217; (or &#8216;top&#8217; and &#8216;bottom&#8217;) properties (indeed, the JavaDoc as of today <a href="http://download.oracle.com/javafx/2.0/api/javafx/scene/control/SplitPane.html">still refers to this API</a>). These were synonomous &#8211; if you set &#8216;top&#8217; and &#8216;bottom&#8217;, they were literally copied to the &#8216;left&#8217; and &#8216;right&#8217; code, and our SplitPaneSkin just knew to draw with the items stacked vertically, rather than to lay them out horizontally.</p>
<p><img class="aligncenter size-full wp-image-1243" title="splitPane1" src="http://fxexperience.com/wp-content/uploads/2011/06/splitPane1.png" alt="" width="610" height="315" /></p>
<p><span id="more-1242"></span>In the very, very late stages of the JavaFX 2.0 EA program, we decided we didn&#8217;t really like this API all that much. The concept of having both left/right and top/bottom just didn&#8217;t really gel with us. After some discussion, we thought we&#8217;d use the same API style as we do elsewhere in JavaFX 2.0: expose a collection and allow for developers to place their content into it. This mean that to use a SplitPane, you&#8217;d use code such as the following:</p>
<pre class="brush: java; title: ; notranslate">
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class SplitPaneSample extends Application {

    @Override public void start(Stage stage) {
        HBox hbox = new HBox(20);
        hbox.setTranslateX(20);
        hbox.setTranslateY(20);

        SplitPane splitPane1 = new SplitPane();
        splitPane1.setPrefSize(200, 200);
        final Button l = new Button(&quot;Left Button&quot;);
        final Button r = new Button(&quot;Right Button&quot;);
        splitPane1.getItems().addAll(l, r);
        hbox.getChildren().add(splitPane1);

        Scene scene = new Scene(new Group(hbox), 560, 240);
        scene.setFill(Color.GHOSTWHITE);
        stage.setScene(scene);
        stage.setTitle(&quot;SplitPane&quot;);
        stage.setVisible(true);
    }

    public static void main(String[] args) {
        launch(args);
    }
}
</pre>
<p>The main segment of code is the middle block, where we create a SplitPane, set a preferred size, and add in the two buttons we want to display. I hope you see where I&#8217;m going with this: now that we aren&#8217;t constrained to just having &#8216;left&#8217; and &#8216;right&#8217; nodes, we can actually put in any number of nodes into the items collection &#8211; and the SplitPane will automatically create N &#8211; 1 dividers. For example:</p>
<pre class="brush: java; title: ; notranslate">
        SplitPane splitPane2 = new SplitPane();
        splitPane2.setPrefSize(300, 200);
        final Button l2 = new Button(&quot;Left Button&quot;);
        final Button c2 = new Button(&quot;Center Button&quot;);
        final Button r2 = new Button(&quot;Right Button&quot;);
        splitPane2.getItems().addAll(l2, c2, r2);
        hbox.getChildren().add(splitPane2);
</pre>
<p>The result of this code is shown in the second SplitPane you see in the picture above. Now, a SplitPane wouldn&#8217;t be all that great if it only allows for the SplitPane content to run in a horizontal flow, so we also support setting an orientation on the SplitPane. As mentioned, the default value is HORIZONTAL, which is what you see in the screenshot above. You can also set it to VERTICAL, which you can see in the screenshot below.</p>
<pre class="brush: java; title: ; notranslate">
        SplitPane splitPane1 = new SplitPane();
        splitPane1.setOrientation(Orientation.VERTICAL);
        splitPane1.setPrefSize(200, 200);
        final Button l1 = new Button(&quot;Left Button&quot;);
        final Button r1 = new Button(&quot;Right Button&quot;);
        splitPane1.getItems().addAll(l1, r1);
        hbox.getChildren().add(splitPane1);

        SplitPane splitPane2 = new SplitPane();
        splitPane2.setOrientation(Orientation.VERTICAL);
        splitPane2.setPrefSize(300, 200);
        final Button l2 = new Button(&quot;Left Button&quot;);
        final Button c2 = new Button(&quot;Center Button&quot;);
        final Button r2 = new Button(&quot;Right Button&quot;);
        splitPane2.getItems().addAll(l2, c2, r2);
        hbox.getChildren().add(splitPane2);
</pre>
<p><img class="aligncenter size-full wp-image-1244" title="splitPane2" src="http://fxexperience.com/wp-content/uploads/2011/06/splitPane2.png" alt="" width="604" height="320" /></p>
<p>Now, you can&#8217;t have a single SplitPane with multiple orientations. However, you can quite easily embed one SplitPane inside another to get this effect:</p>
<pre class="brush: java; title: ; notranslate">
        SplitPane splitPane1 = new SplitPane();
        splitPane1.setOrientation(Orientation.VERTICAL);
        splitPane1.setPrefSize(200, 200);
        final Button l1 = new Button(&quot;Top Button&quot;);
        final Button r1 = new Button(&quot;Bottom Button&quot;);
        splitPane1.getItems().addAll(l1, r1);

        SplitPane splitPane2 = new SplitPane();
        splitPane2.setOrientation(Orientation.HORIZONTAL);
        splitPane2.setPrefSize(300, 200);
        final Button c2 = new Button(&quot;Center Button&quot;);
        final Button r2 = new Button(&quot;Right Button&quot;);
        splitPane2.getItems().addAll(splitPane1, c2, r2);
        hbox.getChildren().add(splitPane2);
</pre>
<p>With the code above, you&#8217;ll end up with something a little like this:<br />
<img class="aligncenter size-full wp-image-1245" title="splitPane3" src="http://fxexperience.com/wp-content/uploads/2011/06/splitPane3.png" alt="" width="393" height="313" /></p>
<p>I hope this helps to introduce you to our brand new SplitPane control, and I look forward to see you using it in your applications soon!</p>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2011/06/splitpane-in-javafx-2-0/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Indeterminate CheckBox</title>
		<link>http://fxexperience.com/2011/06/indeterminate-checkbox/</link>
		<comments>http://fxexperience.com/2011/06/indeterminate-checkbox/#comments</comments>
		<pubDate>Fri, 03 Jun 2011 22:54:29 +0000</pubDate>
		<dc:creator>Richard Bair</dc:creator>
				<category><![CDATA[Controls]]></category>
		<category><![CDATA[Tips n' Tricks]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=1224</guid>
		<description><![CDATA[The CheckBox in JavaFX can be configured for two states (selected, or not) or three states (selected, unselected, or indeterminate). This indeterminate state is often useful when a checkbox is being used in a TreeView, for example. You might be implementing a tree view showing which features are installed, and need to toggle to an [...]]]></description>
			<content:encoded><![CDATA[<p>The CheckBox in JavaFX can be configured for two states (selected, or not) or three states (selected, unselected, or indeterminate). This indeterminate state is often useful when a checkbox is being used in a TreeView, for example. You might be implementing a tree view showing which features are installed, and need to toggle to an indeterminate state for the branch of some of the children are selected, and some are not.</p>
<p><img class="aligncenter size-full wp-image-1226" title="IndeterminateCheckBox" src="http://fxexperience.com/wp-content/uploads/2011/06/IndeterminateCheckBox.png" alt="" width="400" height="422" /><br />
<span id="more-1224"></span><br />
In JavaFX, putting the CheckBox into a mode where it can be indeterminate is quite simple, simply set the allowIndeterminate property to true:</p>
<pre class="brush: java; title: ; notranslate">
        CheckBox cb = new CheckBox(&quot;Indeterminate CheckBox&quot;);
        cb.setAllowIndeterminate(true);
</pre>
<p>You manipulate the state of the check box through the use of two properties: selected and indeterminate. If allowIndeterminate is true, then as the user clicks on the check box, it will cycle through three state combinations:</p>
<ul>
<li>selected = true, indeterminate = false</li>
<li>selected = false, indeterminate = true</li>
<li>selected = false, indeterminate = false</li>
</ul>
<p>Here is a full example:</p>
<pre class="brush: java; title: ; notranslate">
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class IndeterminateCheckBox extends Application {
    Label label;
    CheckBox cbox;

    @Override public void start(Stage stage) {
        label = new Label();

        cbox = new CheckBox(&quot;Indeterminate CheckBox&quot;);
        cbox.setIndeterminate(true);
        cbox.setAllowIndeterminate(true);

        ChangeListener&lt;Boolean&gt; listener = new ChangeListener&lt;Boolean&gt;() {
            @Override
            public void changed(ObservableValue&lt;? extends Boolean&gt; prop, Boolean old, Boolean val) {
                updateLabel();
            }
        };

        cbox.selectedProperty().addListener(listener);
        cbox.indeterminateProperty().addListener(listener);
        updateLabel();

        VBox vbox = new VBox(7);
        vbox.setAlignment(Pos.CENTER);
        vbox.getChildren().addAll(label, cbox);
        Scene scene = new Scene(vbox, 400, 400);
        stage.setTitle(&quot;Hello CheckBox&quot;);
        stage.setScene(scene);
        stage.setVisible(true);
    }

    private void updateLabel() {
        final String txt = cbox.isIndeterminate() ? &quot;The check box is indeterminate&quot; :
                cbox.isSelected() ? &quot;The check box is selected&quot; :
                &quot;The check box is not selected&quot;;

        label.setText(txt);
    }

    public static void main(String[] args) {
        launch(args);
    }
}
</pre>
<p>So, whenever either the indeterminate or selected property changes, we update the label&#8217;s text to match. This is kind of gross though (now that I&#8217;ve typed it out), because I have to attach a listener to two different properties. It would be nicer if there were a proper event handler that I could attach to the CheckBox which would be notified whenever indeterminate or selected changed.</p>
<p>I&#8217;ve filed a JIRA <a href="http://javafx-jira.kenai.com/browse/RT-13992">RT-13992</a> to track this.</p>
<p>Meanwhile, I can also use the high level binding APIs to do this. Here is an alternative implementation.</p>
<pre class="brush: java; title: ; notranslate">
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class IndeterminateCheckBox2 extends Application {
    @Override public void start(Stage stage) {
        CheckBox cbox = new CheckBox(&quot;Indeterminate CheckBox&quot;);
        cbox.setIndeterminate(true);
        cbox.setAllowIndeterminate(true);

        Label label = new Label();
        label.textProperty().bind(
            Bindings.when(cbox.indeterminateProperty()).
                then(&quot;The check box is indeterminate&quot;).
                otherwise(
                    Bindings.when(cbox.selectedProperty()).
                        then(&quot;The check box is selected&quot;).
                        otherwise(&quot;The check box is not selected&quot;))
        );

        VBox vbox = new VBox(7);
        vbox.setAlignment(Pos.CENTER);
        vbox.getChildren().addAll(label, cbox);
        Scene scene = new Scene(vbox, 400, 400);
        stage.setTitle(&quot;Hello CheckBox&quot;);
        stage.setScene(scene);
        stage.setVisible(true);
    }

    public static void main(String[] args) {
        launch(args);
    }
}
</pre>
<p>And that&#8217;s pretty darn slick, IMHO.</p>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2011/06/indeterminate-checkbox/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Maps in JavaFX 2.0</title>
		<link>http://fxexperience.com/2011/05/maps-in-javafx-2-0/</link>
		<comments>http://fxexperience.com/2011/05/maps-in-javafx-2-0/#comments</comments>
		<pubDate>Wed, 01 Jun 2011 02:11:10 +0000</pubDate>
		<dc:creator>Jasper Potts</dc:creator>
				<category><![CDATA[Controls]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Web Services]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=1170</guid>
		<description><![CDATA[Something that you see more and more with client applications at the moment is embedded map components. This blog will show you how to embed Google, Yahoo or Bing maps in your JavaFX application. Download a copy of the application and try it out. The jar file is linked below, just download it and double [...]]]></description>
			<content:encoded><![CDATA[<p>Something that you see more and more with client applications at the moment is embedded map components. This blog will show you how to embed Google, Yahoo or Bing maps in your JavaFX application.</p>
<div id="attachment_1171" class="wp-caption aligncenter" style="width: 610px"><a href="http://fxexperience.com/wp-content/uploads/2011/05/Screen-shot-2011-05-27-at-6.54.48-PM.png"><img class="size-full wp-image-1171 " title="Screen shot 2011-05-27 at 6.54.48 PM" src="http://fxexperience.com/wp-content/uploads/2011/05/Screen-shot-2011-05-27-at-6.54.48-PM.png" alt="" width="600" height="497" /></a><p class="wp-caption-text">Google Maps in JavaFX 2.0</p></div>
<p>Download a copy of the application and try it out. The jar file is linked below, just download it and double click. This requires you to have the JavaFX 2.0 beta runtime installed.</p>
<p style="color: #ff5400; text-align: center;"><strong><br />
<a href="http://fxexperience.com/applications/WebMap.jar"><img style="text-align: center; padding: 0; margin: 0;" src="http://fxexperience.com/applications/jar-icon.png" alt="" /><br />
Download double clickable jar</a></strong></p>
<p><span id="more-1170"></span></p>
<h3>Using the Application</h3>
<p>The toolbar at the top of the application is all JavaFX controls, it communicates with the web map by calling JavaScript functions. Clicking on the Road,Satellite, Hybrid or Terrain sends commands to the JavaScript map library to change the map style. The Google, Yahoo and Bing buttons change the HTML page to use different map providers. As you type in the location field it navigates to the location, try &#8220;London&#8221; or &#8220;New York&#8221; for example. The zoom in/out buttons call JavaScript functions to change the map zoom level.</p>
<div id="attachment_1185" class="wp-caption aligncenter" style="width: 610px"><a href="http://fxexperience.com/wp-content/uploads/2011/05/Screen-shot-2011-05-27-at-6.55.11-PM.png"><img class="size-full wp-image-1185" title="Yahoo Maps" src="http://fxexperience.com/wp-content/uploads/2011/05/Screen-shot-2011-05-27-at-6.55.11-PM.png" alt="" width="600" height="497" /></a><p class="wp-caption-text">Yahoo Maps in JavaFX 2.0</p></div>
<div id="attachment_1186" class="wp-caption aligncenter" style="width: 610px"><a href="http://fxexperience.com/wp-content/uploads/2011/05/Screen-shot-2011-05-27-at-6.55.19-PM.png"><img class="size-full wp-image-1186" title="Bing Maps" src="http://fxexperience.com/wp-content/uploads/2011/05/Screen-shot-2011-05-27-at-6.55.19-PM.png" alt="" width="600" height="497" /></a><p class="wp-caption-text">Bing Maps in JavaFX 2.0</p></div>
<h3>How it&#8217;s Built</h3>
<p>There are three parts to the application: the HTML and JavaScript code, the JavaFX application and the css styling.</p>
<h4>The HTML Code</h4>
<pre class="brush: xml; title: ; notranslate">
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;initial-scale=1.0, user-scalable=no&quot; /&gt;
&lt;style type=&quot;text/css&quot;&gt;
  html { height: 100% }
  body { height: 100%; margin: 0px; padding: 0px }
  #map_canvas { height: 100%; background-color: #666970; }
&lt;/style&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://maps.google.com/maps/api/js?sensor=false&quot;&gt;
&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
  function initialize() {
    var latlng = new google.maps.LatLng(37.39822, -121.9643936);
    var myOptions = {
      zoom: 14,
      center: latlng,
      mapTypeId: google.maps.MapTypeId.ROADMAP,
      mapTypeControl: false,
      navigationControl: false,
      streetViewControl: false,
      backgroundColor: &quot;#666970&quot;
    };

    document.geocoder = new google.maps.Geocoder();
    document.map = new google.maps.Map(document.getElementById(&quot;map_canvas&quot;),myOptions);

    document.zoomIn = function zoomIn() {
    	var zoomLevel = document.map.getZoom();
    	if (zoomLevel &lt;= 20) document.map.setZoom(zoomLevel + 1);
    }

    document.zoomOut = function zoomOut() {
    	var zoomLevel = document.map.getZoom();
    	if (zoomLevel &gt; 0) document.map.setZoom(zoomLevel - 1);
    }

    document.setMapTypeRoad = function setMapTypeRoad() {
    	document.map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
    }
    document.setMapTypeSatellite = function setMapTypeSatellite() {
    	document.map.setMapTypeId(google.maps.MapTypeId.SATELLITE);
    }
    document.setMapTypeHybrid = function setMapTypeHybrid() {
    	document.map.setMapTypeId(google.maps.MapTypeId.HYBRID);
    }
    document.setMapTypeTerrain = function setMapTypeTerrain() {
    	document.map.setMapTypeId(google.maps.MapTypeId.TERRAIN);
    }

    document.goToLocation = function goToLocation(searchString) {
        document.geocoder.geocode( {'address': searchString}, function(results, status) {
      	  if (status == google.maps.GeocoderStatus.OK) {
            document.map.setCenter(results[0].geometry.location);
          } else {
            alert(&quot;Geocode was not successful for the following reason: &quot; + status);
          }
        });
    }
  }
&lt;/script&gt;
&lt;/head&gt;
&lt;body onload=&quot;initialize()&quot;&gt;
  &lt;div id=&quot;map_canvas&quot; style=&quot;width:100%; height:100%&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>This is the code for using Google Maps API, there is similar code for Yahoo and Bing in the attached Netbeans project below. This is very basic use of the Google Maps API to create a full page map and provide some constant JavaScript functions that the JavaFX application can call. You could have called the Google API directly from JavaFX but it seemed simpler to provide a basic abstraction layer in JavaScript so the only difference the JavaFX app sees is the URL. The other two APIs Yahoo and Bing require that you register to get a API key to be able to use them. The code is provided you just need to insert you API keys into the HTML files.</p>
<h4>The JavaFX Code</h4>
<p>The most basic application without the toolbar to just show the map would be:</p>
<pre class="brush: java; title: ; notranslate">
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebMap extends Application {

    @Override public void start(Stage stage) {
        // create web engine and view
        final WebEngine webEngine = new WebEngine(getClass().getResource(&quot;googlemap.html&quot;).toString());
        final WebView webView = new WebView(webEngine);
        // create scene
        stage.setTitle(&quot;Web Map&quot;);
        Scene scene = new Scene(webView,1000,700, Color.web(&quot;#666970&quot;));
        stage.setScene(scene);
        // show stage
        stage.setVisible(true);
    }

    static { // use system proxy settings when standalone application
        System.setProperty(&quot;java.net.useSystemProxies&quot;, &quot;true&quot;);
    }

    public static void main(String[] args){
        Application.launch(args);
    }
}
</pre>
<p>The next step is to create the toolbar, this is just a bunch of JavaFX controls in a Toolbar with actions that call the JavaScript functions in the HTML page.</p>
<pre class="brush: java; title: ; notranslate">
import ....

public class WebMap extends Application {
    private Timeline locationUpdateTimeline;

    @Override public void start(Stage stage) {
        // create web engine and view
        final WebEngine webEngine = new WebEngine(
                getClass().getResource(&quot;googlemap.html&quot;).toString());
        final WebView webView = new WebView(webEngine);
        // create map type buttons
        final ToggleGroup mapTypeGroup = new ToggleGroup();
        final ToggleButton road = new ToggleButton(&quot;Road&quot;);
        road.setSelected(true);
        road.setToggleGroup(mapTypeGroup);
        final ToggleButton satellite = new ToggleButton(&quot;Satellite&quot;);
        satellite.setToggleGroup(mapTypeGroup);
        final ToggleButton hybrid = new ToggleButton(&quot;Hybrid&quot;);
        hybrid.setToggleGroup(mapTypeGroup);
        final ToggleButton terrain = new ToggleButton(&quot;Terrain&quot;);
        terrain.setToggleGroup(mapTypeGroup);
        mapTypeGroup.selectedToggleProperty().addListener(
                            new ChangeListener&lt;Toggle&gt;() {
            public void changed(
                    ObservableValue&lt;? extends Toggle&gt; observableValue,
                    Toggle toggle, Toggle toggle1) {
                if (road.isSelected()) {
                    webEngine.executeScript(&quot;document.setMapTypeRoad()&quot;);
                } else if (satellite.isSelected()) {
                    webEngine.executeScript(&quot;document.setMapTypeSatellite()&quot;);
                } else if (hybrid.isSelected()) {
                    webEngine.executeScript(&quot;document.setMapTypeHybrid()&quot;);
                } else if (terrain.isSelected()) {
                    webEngine.executeScript(&quot;document.setMapTypeTerrain()&quot;);
                }
            }
        });
        // add map source toggles
        ToggleGroup mapSourceGroup = new ToggleGroup();
        final ToggleButton google = new ToggleButton(&quot;Google&quot;);
        google.setSelected(true);
        google.setToggleGroup(mapSourceGroup);
        final ToggleButton yahoo = new ToggleButton(&quot;Yahoo&quot;);
        yahoo.setToggleGroup(mapSourceGroup);
        final ToggleButton bing = new ToggleButton(&quot;Bing&quot;);
        bing.setToggleGroup(mapSourceGroup);
        // listen to selected source
        mapSourceGroup.selectedToggleProperty().addListener(
                            new ChangeListener&lt;Toggle&gt;() {
            public void changed(
                    ObservableValue&lt;? extends Toggle&gt; observableValue,
                    Toggle toggle, Toggle toggle1) {
                terrain.setDisable(true);
                if (google.isSelected()) {
                    terrain.setDisable(false);
                    webEngine.load(
                          getClass().getResource(&quot;googlemap.html&quot;).toString());
                } else if (yahoo.isSelected()) {
                    webEngine.load(
                          getClass().getResource(&quot;bingmap.html&quot;).toString());
                } else if (bing.isSelected()) {
                    webEngine.load(
                          getClass().getResource(&quot;yahoomap.html&quot;).toString());
                }
                mapTypeGroup.selectToggle(road);
            }
        });
        // add search
        final TextBox searchBox = new TextBox(&quot;95054&quot;);
        searchBox.setColumns(12);
        searchBox.setPromptText(&quot;Search&quot;);
        searchBox.rawTextProperty().addListener(new ChangeListener&lt;String&gt;() {
            public void changed(
                    ObservableValue&lt;? extends String&gt; observableValue,
                    String s, String s1) {
                // delay location updates to we don't go too fast file typing
                if (locationUpdateTimeline!=null) locationUpdateTimeline.stop();
                locationUpdateTimeline = new Timeline();
                locationUpdateTimeline.getKeyFrames().add(
                    new KeyFrame(new Duration(400),
                            new EventHandler&lt;ActionEvent&gt;() {
                        public void handle(ActionEvent actionEvent) {
                            webEngine.executeScript(&quot;document.goToLocation(\&quot;&quot;+
                                    searchBox.getRawText()+&quot;\&quot;)&quot;);
                        }
                    })
                );
                locationUpdateTimeline.play();
            }
        });
        Button zoomIn = new Button(&quot;Zoom In&quot;);
        zoomIn.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
            public void handle(ActionEvent actionEvent) {
                webEngine.executeScript(&quot;document.zoomIn()&quot;);
            }
        });
        Button zoomOut = new Button(&quot;Zoom Out&quot;);
        zoomOut.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
            public void handle(ActionEvent actionEvent) {
                webEngine.executeScript(&quot;document.zoomOut()&quot;);
            }
        });
        // create toolbar
        ToolBar toolBar = new ToolBar();
        toolBar.getStyleClass().add(&quot;map-toolbar&quot;);
        toolBar.getItems().addAll(
                road, satellite, hybrid, terrain,
                createSpacer(),
                google, yahoo, bing,
                createSpacer(),
                new Label(&quot;Location:&quot;), searchBox, zoomIn, zoomOut);
        // create root
        BorderPane root = new BorderPane();
        root.getStyleClass().add(&quot;map&quot;);
        root.setCenter(webView);
        root.setTop(toolBar);
        // create scene
        stage.setTitle(&quot;Web Map&quot;);
        Scene scene = new Scene(root,1000,700, Color.web(&quot;#666970&quot;));
        stage.setScene(scene);
        scene.getStylesheets().add(&quot;/webmap/WebMap.css&quot;);
        // show stage
        stage.setVisible(true);
    }

    private Node createSpacer() {
        Region spacer = new Region();
        HBox.setHgrow(spacer, Priority.ALWAYS);
        return spacer;
    }

    static { // use system proxy settings when standalone application
        System.setProperty(&quot;java.net.useSystemProxies&quot;, &quot;true&quot;);
    }

    public static void main(String[] args){
        Application.launch(args);
    }
}
</pre>
<h4>The CSS Code</h4>
<p>The last part of the application is a little css to style the toolbar and make it look dark to stand out from the maps.</p>
<pre class="brush: css; title: ; notranslate">
.map{
    -fx-background-color: #666970;
}
.map-toolbar .button, .map-toolbar .toggle-button, .map-toolbar .label {
    -fx-text-fill: white;
    -fx-background-radius: 0;
}
.map-toolbar{
    -fx-base: #505359;
    -fx-background: #505359;
    -fx-shadow-highlight-color: transparent;
    -fx-spacing: 5;
    -fx-padding: 4 4 4 4;
    -fx-background-color: linear (0%,0%) to (0%,100%) stops
                (0%,#919398)
                (4%,#919398)
                (4.1%,#666970)
                (66%,#494d53)
                (94%,#3c3f46)
                (94.1%,#515151);
}
</pre>
<h3>Download Source</h3>
<p style="color: #ff5400; text-align: center;"><strong><br />
<a href="http://fxexperience.com/applications/WebMapProject.zip"><img style="text-align: center; padding: 0; margin: 0;" src="http://fxexperience.com/applications/project-zip.png" alt="" /><br />
Download Netbeans Project</a></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2011/05/maps-in-javafx-2-0/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>ListView Multiple Selection</title>
		<link>http://fxexperience.com/2010/07/listview-multiple-selection/</link>
		<comments>http://fxexperience.com/2010/07/listview-multiple-selection/#comments</comments>
		<pubDate>Sat, 24 Jul 2010 07:11:55 +0000</pubDate>
		<dc:creator>Jonathan Giles</dc:creator>
				<category><![CDATA[Controls]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=948</guid>
		<description><![CDATA[One of the things that would be really nice to have in the virtualised ListView and TreeView JavaFX controls, not to mention future controls like TableView, is multiple selection. Certain kinds of apps just can not exist without multiple selection in fact. So, unsurprisingly, today I got an email from a user of JavaFX, who [...]]]></description>
			<content:encoded><![CDATA[<p>One of the things that would be really nice to have in the virtualised ListView and TreeView JavaFX controls, not to mention future controls like TableView, is multiple selection. Certain kinds of apps just can not exist without multiple selection in fact.</p>
<p>So, unsurprisingly, today I got an email from a user of JavaFX, who claims to be a fan of FX Experience (hi Keith!), who was needing multiple selection for his work. I didn&#8217;t actually think it could be done very easily (and one of my main jobs is working on these controls, so I should know), but I spent a bit of time looking into it, and it turns out that it&#8217;s actually quite possible, with a number of warnings and rough edges, and also the use of a little bit of unpublished API. As long as you&#8217;re promising to not tell anyone, I thought I&#8217;d share this code&#8230;.but just with you, so shhh <img src='http://fxexperience.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p><span id="more-948"></span></p>
<p>What I present to you today therefore is to be considered <strong>proof of concept only</strong>. Using this in your day job is considered risky, as some of the code may not work in future releases. Being a proof of concept, the code below is not fully implemented (which I&#8217;ll detail more shortly), and performance may degrade somewhat as the number of selected items increases. No attempt at performance optimisation has been made. Proceed at your own risk <img src='http://fxexperience.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  I hope that I&#8217;ve sufficiently scared you.</p>
<p>I wanted to briefly show the code, and then just provide you with a jar file to download and use in your own projects. Firstly though, here&#8217;s a screenshot I sent to Keith to detail what he should expect to see when he runs the new control:</p>
<p><img class="aligncenter size-full wp-image-949" title="listview-multiselect" src="http://fxexperience.com/wp-content/uploads/2010/07/listview-multiselect.png" alt="listview-multiselect" width="359" height="301" /></p>
<p>Do not adjust your TV sets (for those of you, ah, accessing the net via your TV?). The ListView on the right looks odd because the label is rotated inside the cell, to make it apparent that you can get multiple selection even when using a custom cell factory. To get the multiple rows selected I just held down ctrl or shift as I left-clicked, as is usually the case in list controls in other UI toolkits. Here&#8217;s the code I wrote to create the app in the screenshot above:</p>
<pre class="brush: javafx; title: ; notranslate">
Stage {
    title: &quot;Multiple Selection Demo&quot;
    scene: Scene {
        width: 340
        height: 260
        content: [
            HBox {
                spacing: 10
                content: [
                    MultiSelectListView {
                        items: [1..100]
                    },

                    MultiSelectListView {
                        items: [1..100]
                        cellFactory: function() {
                            def cell:ListCell = MultiSelectListCell {
                                node: Label {
                                    rotate: 180
                                    text: bind if (cell.item == null)
                                        then &quot;&quot; else &quot;{cell.item}&quot;;
                                }
                            }
                        }
                    }
                ]
            }
        ]
    }
}
</pre>
<p>Pretty much the kind of code you&#8217;d expect to see when creating a ListView, except the control is called MultiSelectListView, and the ListCell used in the second list is called MultiSelectListCell.</p>
<p>The code for MultiSelectListView is shown below. There is nothing special in here, it simply extends ListView, and adds a bit more API for selectedIndexes and selectedItems, as well as a default cell factory if one isn&#8217;t specified by the user.</p>
<pre class="brush: javafx; title: ; notranslate">
import javafx.scene.control.ListView;
import javafx.scene.control.ListCell;
import javafx.scene.Node;
import javafx.scene.control.Label;

public class MultiSelectListView extends ListView {

    public-read var selectedItems:Object[];

    public var selectedIndexes:Integer[] on replace {
        selectedItems = for (i in selectedIndexes) items[i];
    }

    override var cellFactory = function():ListCell {
        var label:Label;
        def cell:ListCell = MultiSelectListCell {
            onUpdate: function() {
                def item = cell.item;
                if (item == null) {
                    cell.node = null;
                } else if (item instanceof Node) {
                    cell.node = item as Node;
                } else {
                    if (label == null) {
                        label = Label { }
                    }
                    label.text = if (item instanceof String) then item as String else &quot;{item}&quot;;
                    if (cell.node != label) cell.node = label;
                }
            }
        }
    }
}
</pre>
<p>Moving on, we come to MultiSelectListCell, which is where the real warning comes &#8211; this class has unpublished API being used, and we offer no guarantee that it&#8217;ll stay this way. Use it at your own risk, and seriously, don&#8217;t build your business around this API being available. You&#8217;ve been warned. </p>
<p>This class creates a custom MultiSelectListCellSkin, and binds to the selectedIndexes sequence to determine if it is selected or not. When this property changes, it calls some impl_ code to re-evaluate its state, to allow for the background selection colour to be turned on or off as necessary. The rest of the code is pretty straightforward (i.e. don&#8217;t question it) <img src='http://fxexperience.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<pre class="brush: javafx; title: ; notranslate">
import javafx.scene.control.ListCell;
import javafx.scene.control.Skin;
import com.sun.javafx.scene.control.skin.ListCellSkin;
import com.sun.javafx.scene.control.skin.SkinAdapter;
import javafx.util.Sequences;

public class MultiSelectListCell extends ListCell {

    def multiSelected = bind Sequences.indexOf((listView as MultiSelectListView).selectedIndexes, index) != -1 on invalidate {
        impl_pseudoClassStateChanged(&quot;selected&quot;);
    }

    package override function createDefaultSkin():Skin {
        SkinAdapter {
            rootRegion: MultiSelectListCellSkin { }
        }
    }

    override function impl_getPseudoClassState():String[] {
        [
            if (multiSelected) &quot;selected&quot; else null,
            super.impl_getPseudoClassState()
        ]
    }
}
</pre>
<p>MultiSelectListCellSkin is hidden inside MultiSelectListCell (for no particular reason), and its job is simply to just extend ListCellSkin, apply a small bug workaround, and apply a custom behavior, which is where the actual multiple selection magic happens.</p>
<pre class="brush: javafx; title: ; notranslate">
class MultiSelectListCellSkin extends ListCellSkin {
    override var behavior = MultiSelectListCellBehavior { }

    postinit {
        // This fixes an issue where the mouseReleased function is called twice.
        overlay.onMouseReleased = null;
    }
}
</pre>
<p>The final class is a slight extension of the ListCellBehavior class, not surprisingly called MultiSelectListCellBehavior. This class handles the mouse click event, including determining if ctrl or shift is held down, and acting appropriately.</p>
<pre class="brush: javafx; title: ; notranslate">
import com.sun.javafx.scene.control.behavior.ListCellBehavior;
import javafx.util.Sequences;

public class MultiSelectListCellBehavior extends ListCellBehavior {

    override function mouseReleased(e) {
        // Note that list.select will reset selection
        // for out of bounds indexes. So, need to check
        def listCell = skin.control as MultiSelectListCell;
        def listView = listCell.listView as MultiSelectListView;

        // If the mouse event is not contained within this ListCell, then
        // we don't want to react to it.
        if (listCell.contains(e.x, e.y)) {
            if (listCell.index &gt;= sizeof listView.items) return;

            var row = listCell.index;

            if (e.controlDown) {
                if (Sequences.indexOf(listView.selectedIndexes, row) == -1) {
                    insert row into listView.selectedIndexes;
                } else {
                    delete row from listView.selectedIndexes;
                }
            } else if (e.shiftDown) {
                var start = listView.focusedIndex;
                var end = row;
                var range = if (start &lt; end)
                    then [start..end] else [end..start];

                listView.selectedIndexes = range;
            } else {
                delete listView.selectedIndexes;
                insert row into listView.selectedIndexes;
                listView.focus(row);
            }
        }
    }
}
</pre>
<p>That&#8217;s all there is to it. Note that whilst I wrote this, because it is not a &#8216;production-quality&#8217; control, I haven&#8217;t tested it at all, past a few user tests. I&#8217;m sure it&#8217;ll have issues. If you report them to me I&#8217;ll revise this post to ensure the best control we can have. You should also note that this in no way reflects how we&#8217;ll do multiple selection in the future, when it is supported &#8216;natively&#8217;.</p>
<p>Now, on to the warnings I warned you were coming. In general, don&#8217;t use the select() function any more &#8211; just directly manipulate the selectedIndexes sequence. Also, don&#8217;t use the selectedItem / selectedIndex properties any more &#8211; just use the selectedItems and selectedIndexes sequences instead. If you think you&#8217;re going to forget this, it should be possible to just keep overriding more functions / properties to have everything work as expected, but unless I feel sufficiently nagged, I&#8217;ll probably just leave this as a user exercise. Similarly, there is no support for keyboard navigation / multiple selection in this version.</p>
<p>So &#8211; that&#8217;s it really. You can download a <a href="http://www.jonathangiles.net/javafx/ListMultipleSelection.zip">NetBeans project containing this source code</a>, or if you&#8217;re just wanting to use the code as-is, I&#8217;ve also <a href="http://www.jonathangiles.net/javafx/ListMultipleSelection.jar">put up a jar file</a> for you to use directly in your own applications. Feel free to leave comments.</p>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2010/07/listview-multiple-selection/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Custom Cell Caching</title>
		<link>http://fxexperience.com/2010/06/custom-cell-caching/</link>
		<comments>http://fxexperience.com/2010/06/custom-cell-caching/#comments</comments>
		<pubDate>Sat, 26 Jun 2010 06:09:23 +0000</pubDate>
		<dc:creator>Jonathan Giles</dc:creator>
				<category><![CDATA[Controls]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[UI Design]]></category>

		<guid isPermaLink="false">http://fxexperience.com/?p=883</guid>
		<description><![CDATA[Ok, I know we&#8217;ve been going on about custom cells / cell factories a bit recently, but I wanted to do one more post about a very useful topic: caching within cell content. These days &#8216;Hello World&#8217; has been replaced by building a Twitter client, so I&#8217;ve decided to frame this topic in terms of [...]]]></description>
			<content:encoded><![CDATA[<p>Ok, I know we&#8217;ve been going on about custom cells / cell factories a bit recently, but I wanted to do one more post about a very useful topic: caching within cell content.</p>
<p>These days &#8216;Hello World&#8217; has been replaced by building a Twitter client, so I&#8217;ve decided to frame this topic in terms of building a Twitter client. Because I don&#8217;t actually care about the whole web service side of thing, I&#8217;ve neglected to implement the whole &#8216;real data&#8217; / web services aspect of it. If you want to see an actual running implementation with real data, have a look at <span>William Antônio&#8217;s <a href="http://bit.ly/cXsqUR">Twitter client</a>, which is using this ListCell implementation.</span></p>
<p><span><span id="more-883"></span>So, in all the posts to this site related to cells, I&#8217;m sure you&#8217;ve probably come to appreciate the ways in which you should create a ListView or TreeView with custom cell factories. Therefore, what I really want to cover in this post is just the custom cell implementation, and the importance of caching. A Twitter client wouldn&#8217;t be a true client without showing the users profile image, so this is my target for caching. Without caching, each time the cell was updated (i.e. the content changes due to scrolling, or when we scroll a user out of screen and then back in), we&#8217;d have to redownload and load the image. This would lead to considerable lag and a poor user experience. What we need to do is load the image once, cache it, and reuse it whenever the image URL is requested by a cell. At the same time, we don&#8217;t want to run the PC dry of memory by loading all profile images into memory. Enter: <a href="http://java.sun.com/javase/6/docs/api/java/lang/ref/SoftReference.html">SoftReference</a> caching.<br />
</span></p>
<p><span><strong>Word of warning</strong>: I&#8217;m not a caching expert. It is possible that I&#8217;ve done something stupid, and I hope you&#8217;ll let me know, but I believe that the code below should at least be decent. I&#8217;ll happily update this example if anyone gives me useful feedback.</span></p>
<p><span>Check out the code below, and I&#8217;ll continue to discuss it afterwards.</span></p>
<pre class="brush: javafx; title: ; notranslate">
import model.Tweet;

import java.lang.ref.SoftReference;
import java.util.HashMap;

import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.util.Math;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Container;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;

// controls whether the cache is used or not. This _really_ shouldn't be false!
def useCache = true;

// map of String -&gt; SoftReference (of Image)
def map = new HashMap();

def IMAGE_SIZE = 48;

public class TwitterListCell extends ListCell {

    // used to represent the users image
    var imageView:ImageView;

    // a slightly bigger and bolder label for the persons name
    var personName:Label = Label {
        font: Font.font(&quot;Arial&quot;, FontWeight.BOLD, 13);
    }

    // the message label
    var message:Label = Label {
        textWrap: true
    }

    override var node = Container {
        content: bind [ imageView, personName, message ]

        override function getPrefHeight(width:Number):Number {
            def w = listView.width;
            Math.max(IMAGE_SIZE, personName.getPrefHeight(w) + message.getPrefHeight(w));
        }

        override function doLayout():Void {
            var x:Number = -1.5;
            var y:Number = 0;
            var listWidth = listView.width;
            var cellHeight = height;

            // position image
            Container.positionNode(imageView, x, y, IMAGE_SIZE, cellHeight,
                HPos.CENTER, VPos.TOP, false);

            // position text at the same indent position regardless of whether
            // an image exists or not
            x += IMAGE_SIZE + 5;
            var textWidth = listWidth - x;
            var personNameHeight = personName.getPrefHeight(textWidth);
            Container.resizeNode(personName, textWidth, personNameHeight);
            Container.positionNode(personName, x, y, listWidth - x, personNameHeight,
                HPos.LEFT, VPos.TOP, false);

            y += personNameHeight;
            Container.resizeNode(message, textWidth, message.getPrefHeight(textWidth));
            Container.positionNode(message, x, y, listWidth - x, height - personNameHeight,
                HPos.LEFT, VPos.TOP, false);
        }
    }

    override var onUpdate = function():Void {
        var tweet = item as Tweet;

        personName.text = tweet.person.name;
        message.text = tweet.message;

        // image handling
        if (map.containsKey(tweet.person.image)) {
            // the image has possibly been cached, so lets try to get it
            var softRef = map.get(tweet.person.image) as SoftReference;

            // get the image out of the SoftReference wrapper
            var image = softRef.get() as Image;

            // check if it is null - which would be the case if the image had
            // been removed by the garbage collector
            if (image == null) {
                // we need to reload the image
                loadImage(tweet.person.image);
            } else {
                // the image is available, so we can reuse it without the
                // burden of having to download and reload it into memory.
                imageView = ImageView {
                    image: image;
                }
            }
        } else {
            // the image is not cached, so lets load it
            loadImage(tweet.person.image);
        }
    };

    function loadImage(url:String) {
        // create the image and imageview
        var image = Image {
            url: url
            height: IMAGE_SIZE
            preserveRatio: true
            backgroundLoading: true
        }
        imageView = ImageView {
            image: image;
        }

        if (useCache) {
            // put into cache using a SoftReference
            var softRef = new SoftReference(image);
            map.put(url, softRef);
        } else {
            map.remove(url);
        }
    }
}
</pre>
<p><span>You&#8217;ll note that in this example most of the code is pretty standard. A few variables are created for the image and text, and then I&#8217;ve gone the route of laying the content out in a Container, but you can achieve a similar layout using the available layout containers. Following this I have defined an onUpdate function, which is called whenever the cell should be updated. This is usually called due to a user interaction, which may potentially change the Cell.item value, which would of course require an update of the cell&#8217;s visuals.</span></p>
<p><span>The bulk, and most important part, of the onUpdate function deals with loading the users profile image, or retrieving and reusing the cached version of it. Note the use of the global HashMap, which maps between the URL of the users image and the Image itself. Because it is global (i.e. static), this map will be available, and used, by all TwitterListCell instances. Also important to note is that I didn&#8217;t put the ImageView itself into the HashMap as a Node can not be placed in multiple positions in the scenegraph, but an Image can be.</span></p>
<p><span>The rest of the code in this class really just deals with the fact that a SoftReference may clear out it&#8217;s reference to the Image object if the garbage collector needs the memory, in which case we need to reload the image again. The other obvious part is the need to also put the image into the cache if it&#8217;s not already there.</span></p>
<p><span>Shown below is the end result, but remember that there is a working version of this demo in William Antônio&#8217;s <a href="http://bit.ly/cXsqUR">Twitter client</a>, which is a very early work in progress.</span></p>
<p><img src="http://fxexperience.com/wp-content/uploads/2010/06/twitter-listcell1.PNG" alt="twitter-listcell" title="twitter-listcell" width="187" height="366" class="aligncenter size-full wp-image-893" /></p>
<p><span>I hope this might be useful to people, and as always we&#8217;re keen to hear your thoughts and feedback, and what you&#8217;re hoping us to cover. Until next time &#8211; cheers! <img src='http://fxexperience.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </span></p>
]]></content:encoded>
			<wfw:commentRss>http://fxexperience.com/2010/06/custom-cell-caching/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

