Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package ambroafb.general.countcombobox; import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.control.ComboBox; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.Tooltip; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import org.apache.commons.lang3.StringUtils; /** * * @author dato */ public class CountComboBox extends ComboBox<CountComboBoxItem> { private static final int defaultWidth = 500; private boolean isDisableState = false; private String viewableCSSFile = "/styles/css/countcomboboxviewable.css"; private final Tooltip tooltip = new Tooltip();; private final int counter = 1; private final ObservableList<CountComboBoxItem> items = FXCollections.observableArrayList(); private Map<String, CountComboBoxContainer> containers = new HashMap<>(); private Basket basket = new Basket(); private final BiConsumer<String, Integer> numberChangeAction; public CountComboBox() { this.setPrefWidth(defaultWidth); this.setTooltip(tooltip); setCellFactory((ListView<CountComboBoxItem> param) -> new CustomCell()); setButtonCell(new CustomButtonCell()); // Never hide comboBox items listView: this.setSkin(new ComboBoxListViewSkin(this) { @Override protected boolean isHideOnClickEnabled() { return false; } }); this.setItems(items); numberChangeAction = (uniqueId, newNumber) -> { if (newNumber <= 0) { basket.remove(uniqueId); } else { basket.add(uniqueId, newNumber); } refreshButtonCell(); }; addKeyListeners(); } public void fillcomboBox(Supplier<List<CountComboBoxItem>> itemsGenerator, Consumer<ObservableList<CountComboBoxItem>> extraAction) { new Thread(new FetchDataFromDB(itemsGenerator, extraAction)).start(); } private void addKeyListeners() { KeyCode plus = KeyCode.ADD; KeyCode minus = KeyCode.SUBTRACT; setOnKeyPressed((KeyEvent event) -> { KeyCode eventKey = event.getCode(); CountComboBoxItem selectedItem = getSelectionModel().getSelectedItem(); if (selectedItem == null) return; CountComboBoxContainer selectedDrawItem = containers.get(selectedItem.getUniqueIdentifier()); if (eventKey.equals(plus)) { selectedDrawItem.increaseNumberBy(counter); } else if (eventKey.equals(minus)) { selectedDrawItem.decreaseNumberBy(counter); } refreshButtonCell(); }); } public void setBasket(Basket b) { if (b == null || b.isEmpty()) { containers.keySet().stream().forEach((key) -> containers.get(key).numberProperty().set(0)); } else { // Firstly reset all oldValue (change number value to 0). containers.values().forEach((countContainer) -> countContainer.numberProperty().set(0)); Iterator<String> itr = b.getIterator(); String uniqueId = ""; while (itr.hasNext()) { uniqueId = itr.next(); if (containers.containsKey(uniqueId)) { int count = b.getCountFor(uniqueId); containers.get(uniqueId).numberProperty().set(count); } } // ComboBox button cell text will show after any item select in comboBox. So select item index that is last in iterator: for (CountComboBoxItem item : items) { if (item.getUniqueIdentifier().equals(uniqueId)) { getSelectionModel().select(items.indexOf(item)); break; } } } } private void refreshButtonCell() { int selected = getSelectionModel().getSelectedIndex(); if (selected == getItems().size() - 1) { getSelectionModel().selectPrevious(); getSelectionModel().selectNext(); } else { getSelectionModel().selectNext(); getSelectionModel().selectPrevious(); } } public Basket getBasket() { return basket; } /** * The method provides to disabled count comboBox. selection colors are gray, increase and decrease buttons does not work. * @param isDisableState True - if comboBox must be disabled. False - if comboBox must be enabled. */ public void setDisabledState(boolean isDisableState) { this.isDisableState = isDisableState; if (isDisableState) { getStylesheets().add(viewableCSSFile); } else { getStylesheets().remove(viewableCSSFile); } } // Private class private class CustomCell extends ListCell<CountComboBoxItem> { public CustomCell() { super(); } @Override public void updateItem(CountComboBoxItem item, boolean empty) { super.updateItem(item, empty); if (item == null || empty) { setGraphic(null); } else { CountComboBoxContainer container = containers.get(item.getUniqueIdentifier()); container.setViewableState(isDisableState); setGraphic(container); } } } // private button cell private class CustomButtonCell extends ListCell<CountComboBoxItem> { private final String delimiter = ", "; public CustomButtonCell() { super(); } @Override public void updateItem(CountComboBoxItem item, boolean empty) { super.updateItem(item, empty); if (item == null || empty) { // This is importent line by javafx ComboBox class reference. setText(null); } else { String title = ""; for (CountComboBoxItem boxItem : getItems()) { String identifier = boxItem.getUniqueIdentifier(); if (containers.containsKey(identifier)) { CountComboBoxContainer boxDrawItem = containers.get(identifier); title = title.concat(boxDrawItem.nameExpression().getValueSafe()); if (!boxDrawItem.nameExpression().getValueSafe().isEmpty()) { title = title.concat(delimiter); } } } title = StringUtils.substringBeforeLast(title, delimiter); setText(title); tooltip.setText(title); } } } private class FetchDataFromDB implements Runnable { private final Supplier<List<CountComboBoxItem>> itemsGenerator; private final Consumer<ObservableList<CountComboBoxItem>> consumer; public FetchDataFromDB(Supplier<List<CountComboBoxItem>> itemsGenerator, Consumer<ObservableList<CountComboBoxItem>> consumer) { this.itemsGenerator = itemsGenerator; this.consumer = consumer; } @Override public void run() { if (itemsGenerator != null) { List<CountComboBoxItem> itemsList = itemsGenerator.get(); itemsList.forEach((item) -> { CountComboBoxContainer itemContainer = new CountComboBoxContainer(item); itemContainer.setAction(numberChangeAction); containers.put(item.getUniqueIdentifier(), itemContainer); }); Platform.runLater(() -> { items.setAll(itemsList); if (consumer != null) { consumer.accept(items); } }); } } } }