Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX ChoiceBox add separator with type safety

I'm looking to add a separator into a choice box and still retain the type safety.

On all of the examples I've seen, they just do the following:

ChoiceBox<Object> cb =  new ChoiceBox<>();
cb.getItems().addAll("one", "two", new Separator(), "fadfadfasd", "afdafdsfas");

Has anyone come up with a solution to be able to add separators and still retain type safety?

I would expect that if I wanted to add separators, I should be able do something along the following:

ChoiceBox<T> cb = new ChoiceBox<T>();
cb.getSeparators().add(1, new Separator()); // 1 is the index of where the separator should be

I shouldn't have to sacrifice type safety just to add separators.

like image 556
thatjavaguy09 Avatar asked Nov 11 '22 01:11

thatjavaguy09


1 Answers

As already noted, are Separators only supported if added to the items (dirty, dirty). To support them along the lines expected in the question, we need to:

  • add the notion of list of separator to choiceBox
  • make its skin aware of that list

While the former is not a big deal, the latter requires a complete re-write (mostly c&p) of its skin, as everything is tightly hidden in privacy. If the re-write has happened anyway, then it's just a couple of lines more :-)

Just for fun, I'm experimenting with ChoiceBoxX that solves some nasty bugs in its selection handling, so couldn't resist to try.

First, add support to the ChoiceBoxx itself:

/**
 * Adds a separator index to the list. The separator is inserted 
 * after the item with the same index. Client code
 * must keep this list in sync with the data.
 * 
 * @param separator
 */
public final void addSeparator(int separator) {
    if (separatorsList.getValue() == null) {
        separatorsList.setValue(FXCollections.observableArrayList());
    }
    separatorsList.getValue().add(separator);
};

Then some changes in ChoiceBoxXSkin

  • must listen to the separatorsList
  • must expect index-of-menuItem != index-of-choiceItem
  • menuItem must keep its index-of-choiceItem

At its simplest, the listener re-builds the popup, the menuItem stores the dataIndex in its properties and all code that needs to access a popup by its dataIndex is delegated to a method that loops through the menuItems until it finds one that fits:

protected RadioMenuItem getMenuItemFor(int dataIndex) {
    if (dataIndex < 0) return null;
    int loopIndex = dataIndex;
    while (loopIndex < popup.getItems().size()) {
        MenuItem item = popup.getItems().get(loopIndex);

        ObservableMap<Object, Object> properties = item.getProperties();
        Object object = properties.get("data-index");
        if ((object instanceof Integer) && dataIndex == (Integer) object) {
            return item instanceof RadioMenuItem ? (RadioMenuItem)item : null;
        }
        loopIndex++;
    }
    return null;
}
like image 120
kleopatra Avatar answered Nov 23 '22 09:11

kleopatra