JavaFX 2.0 Custom ComboBox

In javaFX 2.0 there is ChoiceBox control it is practically made of 2 items, one is label and other on is ContextMenu. Somewhere I saw somebody said something like “use ChoiceBox with low amount of data and ListView for high amount of data” but he did not said why. I was curious  why not is there some limit or what, so i tried what would happened if I insert at least 100 items in Choice Box.

Lets try code example:

<br />
ChoiceBox&lt;String&gt; cb = new ChoiceBox&lt;String&gt;(FXCollections.observableArrayList(Font.getFamilies())); // just add to scene root
 

I need nice ChoiceBox with font list in application that am currently developing so I decided to take a little test.

When I saw result i know why someone said use it only with low data 10-20 items maximum. You can see result by yourself or on down image.

That really disappointed me i need ChoiceBox with more then 150 fonts (all system fonts + custom fonts) and i don’t want to show big ListView or huge context menu because it looks ugly. After little bit of digging testing and experimenting. I have decided to create my own ComboBox.

Some nice features of ComboBox that i want to see:

  • having scrolling ListView (we all know that ListView can have CellFactory) that is great because i want to see font preview in list
  • some nice animation when showing or hiding ListView
  • setting size of visible ListView
  • getting selectedItem from ComboBox
Here is code for ListComboBox Control with some useful public methods, it has pseudo class showing:
<br />
package playground.control;</p>
<p>import java.util.List;<br />
import javafx.beans.property.BooleanProperty;<br />
import javafx.beans.property.DoubleProperty;<br />
import javafx.beans.value.InvalidationListener;<br />
import javafx.beans.value.ObservableValue;<br />
import javafx.collections.ObservableList;<br />
import javafx.scene.control.Control;<br />
import javafx.scene.control.ListCell;<br />
import javafx.scene.control.ListView;<br />
import javafx.scene.control.MultipleSelectionModel;<br />
import javafx.util.Callback;</p>
<p>/**<br />
 *<br />
 * @author jojorabbit<br />
 *<br />
 */<br />
public class ListComboBox&lt;T&gt; extends Control {</p>
<p>    private static final String LIST_COMBO_BOX_STYLE_CLASS = "list-combo-box";<br />
    protected static final String PSEUDO_CLASS_SHOWING = "showing";<br />
    protected ListView&lt;T&gt; listView;<br />
    protected DoubleProperty visibleHeight = new DoubleProperty(0.0d);<br />
    private BooleanProperty showing = new BooleanProperty() {</p>
<p>        @Override<br />
        protected void invalidated() {<br />
            ListComboBox.this.impl_pseudoClassStateChanged(PSEUDO_CLASS_SHOWING);<br />
        }<br />
    };</p>
<p>    public ListComboBox() {<br />
        init();<br />
        initListeners();<br />
        visibleHeight.set(200.0d); // set default height to 200.0<br />
    }</p>
<p>    public boolean isShowing() {<br />
        return showing.get();<br />
    }</p>
<p>    public BooleanProperty showingProperty() {<br />
        return showing;<br />
    }</p>
<p>    @Override<br />
    public void impl_getPseudoClassState(List&lt;String&gt; list) {<br />
        super.impl_getPseudoClassState(list);<br />
        if(isShowing()) {<br />
            list.add(PSEUDO_CLASS_SHOWING);<br />
        }<br />
    }</p>
<p>    private void init() {<br />
        getStyleClass().add(LIST_COMBO_BOX_STYLE_CLASS);<br />
        listView = new ListView&lt;T&gt;();<br />
        listView.getStyleClass().add("list");<br />
    }</p>
<p>    public void setItems(ObservableList&lt;T&gt; items) {<br />
        listView.setItems(items);<br />
    }</p>
<p>    /**<br />
     * Sets visible size of wrapped ListView<br />
     * @param value<br />
     */<br />
    public void setVisibleHeight(double value) {<br />
        visibleHeight.set(value);<br />
    }</p>
<p>    /**<br />
     * Add cell factory to listView<br />
     * @param value cellFactory<br />
     */<br />
    public void setCellFactory(Callback&lt;ListView&lt;T&gt;, ListCell&lt;T&gt;&gt; value) {<br />
        listView.setCellFactory(value);<br />
    }</p>
<p>    /**<br />
     * Get listView selections Model<br />
     * @return selection model<br />
     */<br />
    public MultipleSelectionModel&lt;T&gt; getSelectionModel() {<br />
        return listView.getSelectionModel();<br />
    }</p>
<p>    /**<br />
     * Get selected item<br />
     * @return selected item<br />
     */<br />
    public T getSelectedItem() {
        return listView.getSelectionModel().getSelectedItem();
    }
    /**
     * Get selected index
     * @return selected index
     */
    public int getSelectedIndex() {
        return listView.getSelectionModel().getSelectedIndex();
    }
    private void initListeners() {
        visibleHeight.addListener(new InvalidationListener&lt;Number&gt;() {
            @Override
            public void invalidated(ObservableValue&lt;? extends Number&gt; observable) {
                listView.setPrefHeight(visibleHeight.get());
                listView.setMinHeight(visibleHeight.get());
                listView.setMaxHeight(visibleHeight.get());
            }
        });
    }
}
All methods are described in comments.
Other things i want to see in my ComboBox are nice label and some arrow pointing down, that can be done with -fx-shape region css property and a little bit of SVG knowledge.
At begging i came out with these skin variables :
<br />
private ListView&lt;T&gt; listView;<br />
private Timeline timeline; // timeline for some animations<br />
private Label selectedLabel; // label<br />
private StackPane arrow; // "holder" for arrow shape<br />
private StackPane line; // "holder" for line shape<br />
private static final Duration ANIMATION_DURATION = Duration.valueOf(300.0d); // default animation duration<br />
The main problem with custom Regions is layoutChildren method, used to layout nodes in custom Region I would say that is “must to override” method if you want to accomplish nice custom layout.
There are two important and useful methods in Region class, i will just mention them for better understanding consult API documentation. Methods are layoutInArea and positionInArea.
After few hours of playing and calculating, then adding some CSS i came out with screenshot below.
Sources can be found in github repository.
Hope you like it and feel free to comment/criticize/request features/request custom nodes/report bugs, etc.

EDIT:  28.06.2011.

I have added some changes to current comboBox, some of them are removing that ugly line, adding new StackPane that holds arrow (styleclass = arrow-container), making line with css -fx-background property, removed line from layout calculations, added arrowContainer to layout calculations, adding mouse event handler only on arrow Stack, so clicking only and only on arrow stack will result “listView – pop-up” to appear.
Here are some changes added to CSS:
<br />
.list-combo-box .arrow-container {<br />
    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
    -fx-background-insets: 0 0 0 0, 0, 1, 2;<br />
    -fx-background-radius: 0 5 5 0, 0 5 5 0, 0 4 4 0, 0 3 3 0; /* top right and bottom right corners are rounded*/
    -fx-alignment: CENTER;<br />
    -fx-padding: 10;<br />
}<br />

Some of changes in source :

<br />
 StackPane arrow = new StackPane();<br />
 arrow.getStyleClass().add("arrow");</p>
<p> arrowContainer = new StackPane();<br />
 arrowContainer.getStyleClass().add("arrow-container");<br />
 arrowContainer.getChildren().add(arrow); 
And some images of control in action, well it looks much better then before with that line. If you see some <p> < br> tags in post i will correct them later.
Hope you like it.
EDIT: 07.07.2011.
Here are some new modifications to combobox it looks and works nice code is git is updated to current version. All comments and critics are welcome and if you find any other useful features fell free to contact me.
Some other features that i want to make are only focus traversing for now maybe i will find something else later.
Here are some images of control with comments.
Hope you like it.

JavaFX 2.0 Deck Pane Control

I experiment a lot with everything including new javaFX. Few weeks ago I had to make some “Container” that can change nodes with fade animation. I was inspired by DeckNode from here.

After making it i noticed that i miss some nice features, so i made new Control with some better features like Scale between node changes etc.

Well DeckPane is simple control that shows only one node at a time. Animations between changes of current visibleNode are FADE, SCALE, PERSPECTIVE defined in enum AnimationMode, animation duration can be set via setAnimationDuration(Duration value), default is 500ms, animation duration represents duration of “in” and “out” part,  so the real duration is 1000ms. Setting current visible node is done with setVisibleNodeID(String nodeId) method. The code is still in debug phase you will see a lot of printouts in console, some parts of code are not so clean.

Here is demo video with  that show functionality of Deck Pane Control. Source can be found in git repository.

Hope you like it and feel free to comment/criticize/request features/report bugs, etc.

Here are the pictures of how project looks in netbeans in case you get any errors.