1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018 |
- /*
- * Copyright 2000-2018 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
- package com.vaadin.ui;
-
- import java.io.Serializable;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Objects;
- import java.util.Optional;
- import java.util.Set;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import java.util.stream.Stream;
-
- import org.jsoup.nodes.Element;
-
- import com.vaadin.data.HasFilterableDataProvider;
- import com.vaadin.data.HasValue;
- import com.vaadin.data.ValueProvider;
- import com.vaadin.data.provider.CallbackDataProvider;
- import com.vaadin.data.provider.DataCommunicator;
- import com.vaadin.data.provider.DataGenerator;
- import com.vaadin.data.provider.DataKeyMapper;
- import com.vaadin.data.provider.DataProvider;
- import com.vaadin.data.provider.ListDataProvider;
- import com.vaadin.event.FieldEvents;
- import com.vaadin.event.FieldEvents.BlurEvent;
- import com.vaadin.event.FieldEvents.BlurListener;
- import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
- import com.vaadin.event.FieldEvents.FocusEvent;
- import com.vaadin.event.FieldEvents.FocusListener;
- import com.vaadin.server.ConnectorResource;
- import com.vaadin.server.KeyMapper;
- import com.vaadin.server.Resource;
- import com.vaadin.server.ResourceReference;
- import com.vaadin.server.SerializableBiPredicate;
- import com.vaadin.server.SerializableConsumer;
- import com.vaadin.server.SerializableFunction;
- import com.vaadin.server.SerializableToIntFunction;
- import com.vaadin.shared.Registration;
- import com.vaadin.shared.data.DataCommunicatorConstants;
- import com.vaadin.shared.ui.combobox.ComboBoxClientRpc;
- import com.vaadin.shared.ui.combobox.ComboBoxConstants;
- import com.vaadin.shared.ui.combobox.ComboBoxServerRpc;
- import com.vaadin.shared.ui.combobox.ComboBoxState;
- import com.vaadin.ui.declarative.DesignAttributeHandler;
- import com.vaadin.ui.declarative.DesignContext;
- import com.vaadin.ui.declarative.DesignFormatter;
-
- import elemental.json.JsonObject;
-
- /**
- * A filtering dropdown single-select. Items are filtered based on user input.
- * Supports the creation of new items when a handler is set by the user.
- *
- * @param <T>
- * item (bean) type in ComboBox
- * @author Vaadin Ltd
- */
- @SuppressWarnings("serial")
- public class ComboBox<T> extends AbstractSingleSelect<T>
- implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier,
- HasFilterableDataProvider<T, String> {
-
- /**
- * A callback method for fetching items. The callback is provided with a
- * non-null string filter, offset index and limit.
- *
- * @param <T>
- * item (bean) type in ComboBox
- * @since 8.0
- */
- @FunctionalInterface
- public interface FetchItemsCallback<T> extends Serializable {
-
- /**
- * Returns a stream of items that match the given filter, limiting the
- * results with given offset and limit.
- * <p>
- * This method is called after the size of the data set is asked from a
- * related size callback. The offset and limit are promised to be within
- * the size of the data set.
- *
- * @param filter
- * a non-null filter string
- * @param offset
- * the first index to fetch
- * @param limit
- * the fetched item count
- * @return stream of items
- */
- public Stream<T> fetchItems(String filter, int offset, int limit);
- }
-
- /**
- * Handler that adds a new item based on user input when the new items
- * allowed mode is active.
- * <p>
- * NOTE 1: If the new item is rejected the client must be notified of the
- * fact via ComboBoxClientRpc or selection handling won't complete.
- * </p>
- * <p>
- * NOTE 2: Selection handling must be completed separately if filtering the
- * data source with the same value won't include the new item in the initial
- * list of suggestions. Failing to do so will lead to selection handling
- * never completing and previous selection remaining on the server.
- * </p>
- *
- * @since 8.0
- * @deprecated Since 8.4 replaced by {@link NewItemProvider}.
- */
- @Deprecated
- @FunctionalInterface
- public interface NewItemHandler extends SerializableConsumer<String> {
- }
-
- /**
- * Provider function that adds a new item based on user input when the new
- * items allowed mode is active. After the new item handling is complete,
- * this function should return {@code Optional.of(text)} for the completion
- * of automatic selection handling. If automatic selection is not wished
- * for, always return {@code Optional.isEmpty()}.
- *
- * @since 8.4
- */
- @FunctionalInterface
- public interface NewItemProvider<T>
- extends SerializableFunction<String, Optional<T>> {
- }
-
- /**
- * Item style generator class for declarative support.
- * <p>
- * Provides a straightforward mapping between an item and its style.
- *
- * @param <T>
- * item type
- * @since 8.0
- */
- protected static class DeclarativeStyleGenerator<T>
- implements StyleGenerator<T> {
-
- private StyleGenerator<T> fallback;
- private Map<T, String> styles = new HashMap<>();
-
- public DeclarativeStyleGenerator(StyleGenerator<T> fallback) {
- this.fallback = fallback;
- }
-
- @Override
- public String apply(T item) {
- return styles.containsKey(item) ? styles.get(item)
- : fallback.apply(item);
- }
-
- /**
- * Sets a {@code style} for the {@code item}.
- *
- * @param item
- * a data item
- * @param style
- * a style for the {@code item}
- */
- protected void setStyle(T item, String style) {
- styles.put(item, style);
- }
- }
-
- private ComboBoxServerRpc rpc = new ComboBoxServerRpc() {
- @Override
- public void createNewItem(String itemValue) {
- // New option entered
- boolean added = false;
- if (itemValue != null && !itemValue.isEmpty()) {
- if (getNewItemProvider() != null) {
- Optional<T> item = getNewItemProvider().apply(itemValue);
- added = item.isPresent();
- // Fixes issue https://github.com/vaadin/framework/issues/11343
- // Update the internal selection state immediately to avoid
- // client side hanging. This is needed for cases that user
- // interaction fires multi events (like adding and deleting)
- // on a new item during the same round trip.
- item.ifPresent(value -> {
- setSelectedItem(value, true);
- getDataCommunicator().reset();
- });
- } else if (getNewItemHandler() != null) {
- getNewItemHandler().accept(itemValue);
- // Up to the user to tell if no item was added.
- added = true;
- }
- }
-
- if (!added) {
- // New item was not handled.
- getRpcProxy(ComboBoxClientRpc.class).newItemNotAdded(itemValue);
- }
- }
-
- @Override
- public void setFilter(String filterText) {
- getState().currentFilterText = filterText;
- filterSlot.accept(filterText);
- }
- };
-
- /**
- * Handler for new items entered by the user.
- */
- @Deprecated
- private NewItemHandler newItemHandler;
-
- /**
- * Provider function for new items entered by the user.
- */
- private NewItemProvider<T> newItemProvider;
-
- private StyleGenerator<T> itemStyleGenerator = item -> null;
-
- private SerializableConsumer<String> filterSlot = filter -> {
- // Just ignore when neither setDataProvider nor setItems has been called
- };
-
- /**
- * Constructs an empty combo box without a caption. The content of the combo
- * box can be set with {@link #setDataProvider(DataProvider)} or
- * {@link #setItems(Collection)}
- */
- public ComboBox() {
- this(new DataCommunicator<T>() {
- @Override
- protected DataKeyMapper<T> createKeyMapper(
- ValueProvider<T, Object> identifierGetter) {
- return new KeyMapper<T>(identifierGetter) {
- @Override
- public void remove(T removeobj) {
- // never remove keys from ComboBox to support selection
- // of items that are not currently visible
- }
- };
- }
- });
- }
-
- /**
- * Constructs an empty combo box, whose content can be set with
- * {@link #setDataProvider(DataProvider)} or {@link #setItems(Collection)}.
- *
- * @param caption
- * the caption to show in the containing layout, null for no
- * caption
- */
- public ComboBox(String caption) {
- this();
- setCaption(caption);
- }
-
- /**
- * Constructs a combo box with a static in-memory data provider with the
- * given options.
- *
- * @param caption
- * the caption to show in the containing layout, null for no
- * caption
- * @param options
- * collection of options, not null
- */
- public ComboBox(String caption, Collection<T> options) {
- this(caption);
-
- setItems(options);
- }
-
- /**
- * Constructs and initializes an empty combo box.
- *
- * @param dataCommunicator
- * the data comnunicator to use with this ComboBox
- * @since 8.5
- */
- protected ComboBox(DataCommunicator<T> dataCommunicator) {
- super(dataCommunicator);
- init();
- }
-
- /**
- * Initialize the ComboBox with default settings and register client to
- * server RPC implementation.
- */
- private void init() {
- registerRpc(rpc);
- registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
-
- addDataGenerator(new DataGenerator<T>() {
-
- /**
- * Map for storing names for icons.
- */
- private Map<Object, String> resourceKeyMap = new HashMap<>();
- private int counter = 0;
-
- @Override
- public void generateData(T item, JsonObject jsonObject) {
- String caption = getItemCaptionGenerator().apply(item);
- if (caption == null) {
- caption = "";
- }
- jsonObject.put(DataCommunicatorConstants.NAME, caption);
- String style = itemStyleGenerator.apply(item);
- if (style != null) {
- jsonObject.put(ComboBoxConstants.STYLE, style);
- }
- Resource icon = getItemIcon(item);
- if (icon != null) {
- String iconKey = resourceKeyMap
- .get(getDataProvider().getId(item));
- String iconUrl = ResourceReference
- .create(icon, ComboBox.this, iconKey).getURL();
- jsonObject.put(ComboBoxConstants.ICON, iconUrl);
- }
- }
-
- @Override
- public void destroyData(T item) {
- Object itemId = getDataProvider().getId(item);
- if (resourceKeyMap.containsKey(itemId)) {
- setResource(resourceKeyMap.get(itemId), null);
- resourceKeyMap.remove(itemId);
- }
- }
-
- private Resource getItemIcon(T item) {
- Resource icon = getItemIconGenerator().apply(item);
- if (icon == null || !(icon instanceof ConnectorResource)) {
- return icon;
- }
-
- Object itemId = getDataProvider().getId(item);
- if (!resourceKeyMap.containsKey(itemId)) {
- resourceKeyMap.put(itemId, "icon" + (counter++));
- }
- setResource(resourceKeyMap.get(itemId), icon);
- return icon;
- }
- });
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Filtering will use a case insensitive match to show all items where the
- * filter text is a substring of the caption displayed for that item.
- */
- @Override
- public void setItems(Collection<T> items) {
- ListDataProvider<T> listDataProvider = DataProvider.ofCollection(items);
-
- setDataProvider(listDataProvider);
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Filtering will use a case insensitive match to show all items where the
- * filter text is a substring of the caption displayed for that item.
- */
- @Override
- public void setItems(Stream<T> streamOfItems) {
- // Overridden only to add clarification to javadocs
- super.setItems(streamOfItems);
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Filtering will use a case insensitive match to show all items where the
- * filter text is a substring of the caption displayed for that item.
- */
- @Override
- public void setItems(T... items) {
- // Overridden only to add clarification to javadocs
- super.setItems(items);
- }
-
- /**
- * Sets a list data provider as the data provider of this combo box.
- * Filtering will use a case insensitive match to show all items where the
- * filter text is a substring of the caption displayed for that item.
- * <p>
- * Note that this is a shorthand that calls
- * {@link #setDataProvider(DataProvider)} with a wrapper of the provided
- * list data provider. This means that {@link #getDataProvider()} will
- * return the wrapper instead of the original list data provider.
- *
- * @param listDataProvider
- * the list data provider to use, not <code>null</code>
- * @since 8.0
- */
- public void setDataProvider(ListDataProvider<T> listDataProvider) {
- // Cannot use the case insensitive contains shorthand from
- // ListDataProvider since it wouldn't react to locale changes
- CaptionFilter defaultCaptionFilter = (itemText, filterText) -> itemText
- .toLowerCase(getLocale())
- .contains(filterText.toLowerCase(getLocale()));
-
- setDataProvider(defaultCaptionFilter, listDataProvider);
- }
-
- /**
- * Sets the data items of this listing and a simple string filter with which
- * the item string and the text the user has input are compared.
- * <p>
- * Note that unlike {@link #setItems(Collection)}, no automatic case
- * conversion is performed before the comparison.
- *
- * @param captionFilter
- * filter to check if an item is shown when user typed some text
- * into the ComboBox
- * @param items
- * the data items to display
- * @since 8.0
- */
- public void setItems(CaptionFilter captionFilter, Collection<T> items) {
- ListDataProvider<T> listDataProvider = DataProvider.ofCollection(items);
-
- setDataProvider(captionFilter, listDataProvider);
- }
-
- /**
- * Sets a list data provider with an item caption filter as the data
- * provider of this combo box. The caption filter is used to compare the
- * displayed caption of each item to the filter text entered by the user.
- *
- * @param captionFilter
- * filter to check if an item is shown when user typed some text
- * into the ComboBox
- * @param listDataProvider
- * the list data provider to use, not <code>null</code>
- * @since 8.0
- */
- public void setDataProvider(CaptionFilter captionFilter,
- ListDataProvider<T> listDataProvider) {
- Objects.requireNonNull(listDataProvider,
- "List data provider cannot be null");
-
- // Must do getItemCaptionGenerator() for each operation since it might
- // not be the same as when this method was invoked
- setDataProvider(listDataProvider, filterText -> item -> captionFilter
- .test(getItemCaptionOfItem(item), filterText));
- }
-
- // Helper method for the above to make lambda more readable
- private String getItemCaptionOfItem(T item) {
- String caption = getItemCaptionGenerator().apply(item);
- if (caption == null) {
- caption = "";
- }
- return caption;
- }
-
- /**
- * Sets the data items of this listing and a simple string filter with which
- * the item string and the text the user has input are compared.
- * <p>
- * Note that unlike {@link #setItems(Collection)}, no automatic case
- * conversion is performed before the comparison.
- *
- * @param captionFilter
- * filter to check if an item is shown when user typed some text
- * into the ComboBox
- * @param items
- * the data items to display
- * @since 8.0
- */
- public void setItems(CaptionFilter captionFilter,
- @SuppressWarnings("unchecked") T... items) {
- setItems(captionFilter, Arrays.asList(items));
- }
-
- /**
- * Gets the current placeholder text shown when the combo box would be
- * empty.
- *
- * @see #setPlaceholder(String)
- * @return the current placeholder string, or null if not enabled
- * @since 8.0
- */
- public String getPlaceholder() {
- return getState(false).placeholder;
- }
-
- /**
- * Sets the placeholder string - a textual prompt that is displayed when the
- * select would otherwise be empty, to prompt the user for input.
- *
- * @param placeholder
- * the desired placeholder, or null to disable
- * @since 8.0
- */
- public void setPlaceholder(String placeholder) {
- getState().placeholder = placeholder;
- }
-
- /**
- * Sets whether it is possible to input text into the field or whether the
- * field area of the component is just used to show what is selected. By
- * disabling text input, the comboBox will work in the same way as a
- * {@link NativeSelect}
- *
- * @see #isTextInputAllowed()
- *
- * @param textInputAllowed
- * true to allow entering text, false to just show the current
- * selection
- */
- public void setTextInputAllowed(boolean textInputAllowed) {
- getState().textInputAllowed = textInputAllowed;
- }
-
- /**
- * Returns true if the user can enter text into the field to either filter
- * the selections or enter a new value if new item provider or handler is
- * set (see {@link #setNewItemProvider(NewItemProvider)} (recommended) and
- * {@link #setNewItemHandler(NewItemHandler)} (deprecated)). If text input
- * is disabled, the comboBox will work in the same way as a
- * {@link NativeSelect}
- *
- * @return true if text input is allowed
- */
- public boolean isTextInputAllowed() {
- return getState(false).textInputAllowed;
- }
-
- @Override
- public Registration addBlurListener(BlurListener listener) {
- return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
- BlurListener.blurMethod);
- }
-
- @Override
- public Registration addFocusListener(FocusListener listener) {
- return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
- FocusListener.focusMethod);
- }
-
- /**
- * Returns the page length of the suggestion popup.
- *
- * @return the pageLength
- */
- public int getPageLength() {
- return getState(false).pageLength;
- }
-
- /**
- * Returns the suggestion pop-up's width as a CSS string. By default this
- * width is set to "100%".
- *
- * @see #setPopupWidth
- * @since 7.7
- * @return explicitly set popup width as CSS size string or null if not set
- */
- public String getPopupWidth() {
- return getState(false).suggestionPopupWidth;
- }
-
- /**
- * Sets the page length for the suggestion popup. Setting the page length to
- * 0 will disable suggestion popup paging (all items visible).
- *
- * @param pageLength
- * the pageLength to set
- */
- public void setPageLength(int pageLength) {
- getState().pageLength = pageLength;
- }
-
- /**
- * Returns whether the user is allowed to select nothing in the combo box.
- *
- * @return true if empty selection is allowed, false otherwise
- * @since 8.0
- */
- public boolean isEmptySelectionAllowed() {
- return getState(false).emptySelectionAllowed;
- }
-
- /**
- * Sets whether the user is allowed to select nothing in the combo box. When
- * true, a special empty item is shown to the user.
- *
- * @param emptySelectionAllowed
- * true to allow not selecting anything, false to require
- * selection
- * @since 8.0
- */
- public void setEmptySelectionAllowed(boolean emptySelectionAllowed) {
- getState().emptySelectionAllowed = emptySelectionAllowed;
- }
-
- /**
- * Returns the empty selection caption.
- * <p>
- * The empty string {@code ""} is the default empty selection caption.
- *
- * @return the empty selection caption, not {@code null}
- * @see #setEmptySelectionAllowed(boolean)
- * @see #isEmptySelectionAllowed()
- * @see #setEmptySelectionCaption(String)
- * @see #isSelected(Object)
- * @since 8.0
- */
- public String getEmptySelectionCaption() {
- return getState(false).emptySelectionCaption;
- }
-
- /**
- * Sets the empty selection caption.
- * <p>
- * The empty string {@code ""} is the default empty selection caption.
- * <p>
- * If empty selection is allowed via the
- * {@link #setEmptySelectionAllowed(boolean)} method (it is by default) then
- * the empty item will be shown with the given caption.
- *
- * @param caption
- * the caption to set, not {@code null}
- * @see #isSelected(Object)
- * @since 8.0
- */
- public void setEmptySelectionCaption(String caption) {
- Objects.nonNull(caption);
- getState().emptySelectionCaption = caption;
- }
-
- /**
- * Sets the suggestion pop-up's width as a CSS string. By using relative
- * units (e.g. "50%") it's possible to set the popup's width relative to the
- * ComboBox itself.
- * <p>
- * By default this width is set to "100%" so that the pop-up's width is
- * equal to the width of the combobox. By setting width to null the pop-up's
- * width will automatically expand beyond 100% relative width to fit the
- * content of all displayed items.
- *
- * @see #getPopupWidth()
- * @since 7.7
- * @param width
- * the width
- */
- public void setPopupWidth(String width) {
- getState().suggestionPopupWidth = width;
- }
-
- /**
- * Sets whether to scroll the selected item visible (directly open the page
- * on which it is) when opening the combo box popup or not.
- * <p>
- * This requires finding the index of the item, which can be expensive in
- * many large lazy loading containers.
- *
- * @param scrollToSelectedItem
- * true to find the page with the selected item when opening the
- * selection popup
- */
- public void setScrollToSelectedItem(boolean scrollToSelectedItem) {
- getState().scrollToSelectedItem = scrollToSelectedItem;
- }
-
- /**
- * Returns true if the select should find the page with the selected item
- * when opening the popup.
- *
- * @see #setScrollToSelectedItem(boolean)
- *
- * @return true if the page with the selected item will be shown when
- * opening the popup
- */
- public boolean isScrollToSelectedItem() {
- return getState(false).scrollToSelectedItem;
- }
-
- @Override
- public ItemCaptionGenerator<T> getItemCaptionGenerator() {
- return super.getItemCaptionGenerator();
- }
-
- @Override
- public void setItemCaptionGenerator(
- ItemCaptionGenerator<T> itemCaptionGenerator) {
- super.setItemCaptionGenerator(itemCaptionGenerator);
- }
-
- /**
- * Sets the style generator that is used to produce custom class names for
- * items visible in the popup. The CSS class name that will be added to the
- * item is <tt>v-filterselect-item-[style name]</tt>. Returning null from
- * the generator results in no custom style name being set.
- *
- * @see StyleGenerator
- *
- * @param itemStyleGenerator
- * the item style generator to set, not null
- * @throws NullPointerException
- * if {@code itemStyleGenerator} is {@code null}
- * @since 8.0
- */
- public void setStyleGenerator(StyleGenerator<T> itemStyleGenerator) {
- Objects.requireNonNull(itemStyleGenerator,
- "Item style generator must not be null");
- this.itemStyleGenerator = itemStyleGenerator;
- getDataCommunicator().reset();
- }
-
- /**
- * Gets the currently used style generator that is used to generate CSS
- * class names for items. The default item style provider returns null for
- * all items, resulting in no custom item class names being set.
- *
- * @see StyleGenerator
- * @see #setStyleGenerator(StyleGenerator)
- *
- * @return the currently used item style generator, not null
- * @since 8.0
- */
- public StyleGenerator<T> getStyleGenerator() {
- return itemStyleGenerator;
- }
-
- @Override
- public void setItemIconGenerator(IconGenerator<T> itemIconGenerator) {
- super.setItemIconGenerator(itemIconGenerator);
- }
-
- @Override
- public IconGenerator<T> getItemIconGenerator() {
- return super.getItemIconGenerator();
- }
-
- /**
- * Sets the handler that is called when user types a new item. The creation
- * of new items is allowed when a new item handler has been set. If new item
- * provider is also set, the new item handler is ignored.
- *
- * @param newItemHandler
- * handler called for new items, null to only permit the
- * selection of existing items, all options ignored if new item
- * provider is set
- * @since 8.0
- * @deprecated Since 8.4 use {@link #setNewItemProvider(NewItemProvider)}
- * instead.
- */
- @Deprecated
- public void setNewItemHandler(NewItemHandler newItemHandler) {
- getLogger().log(Level.WARNING,
- "NewItemHandler is deprecated. Please use NewItemProvider instead.");
- this.newItemHandler = newItemHandler;
- getState(true).allowNewItems = newItemProvider != null
- || newItemHandler != null;
- }
-
- /**
- * Sets the provider function that is called when user types a new item. The
- * creation of new items is allowed when a new item provider has been set.
- * If a deprecated new item handler is also set it is ignored in favor of
- * new item provider.
- *
- * @param newItemProvider
- * provider function that is called for new items, null to only
- * permit the selection of existing items or to use a deprecated
- * new item handler if set
- * @since 8.4
- */
- public void setNewItemProvider(NewItemProvider<T> newItemProvider) {
- this.newItemProvider = newItemProvider;
- getState(true).allowNewItems = newItemProvider != null
- || newItemHandler != null;
- }
-
- /**
- * Returns the handler called when the user enters a new item (not present
- * in the data provider).
- *
- * @return new item handler or null if none specified
- * @deprecated Since 8.4 use {@link #getNewItemProvider()} instead.
- */
- @Deprecated
- public NewItemHandler getNewItemHandler() {
- return newItemHandler;
- }
-
- /**
- * Returns the provider function that is called when the user enters a new
- * item (not present in the data provider).
- *
- * @since 8.4
- * @return new item provider or null if none specified
- */
- public NewItemProvider<T> getNewItemProvider() {
- return newItemProvider;
- }
-
- // HasValue methods delegated to the selection model
-
- @Override
- public Registration addValueChangeListener(
- HasValue.ValueChangeListener<T> listener) {
- return addSelectionListener(event -> listener
- .valueChange(new ValueChangeEvent<>(event.getComponent(), this,
- event.getOldValue(), event.isUserOriginated())));
- }
-
- @Override
- protected ComboBoxState getState() {
- return (ComboBoxState) super.getState();
- }
-
- @Override
- protected ComboBoxState getState(boolean markAsDirty) {
- return (ComboBoxState) super.getState(markAsDirty);
- }
-
- @Override
- protected void updateSelectedItemState(T value) {
- super.updateSelectedItemState(value);
-
- updateSelectedItemCaption(value);
- updateSelectedItemIcon(value);
- }
-
- private void updateSelectedItemCaption(T value) {
- String selectedCaption = null;
- if (value != null) {
- selectedCaption = getItemCaptionGenerator().apply(value);
- }
- getState().selectedItemCaption = selectedCaption;
- }
-
- private void updateSelectedItemIcon(T value) {
- String selectedItemIcon = null;
- if (value != null) {
- Resource icon = getItemIconGenerator().apply(value);
- if (icon != null) {
- if (icon instanceof ConnectorResource) {
- if (!isAttached()) {
- // Deferred resource generation.
- return;
- }
- setResource("selected", icon);
- }
- selectedItemIcon = ResourceReference
- .create(icon, ComboBox.this, "selected").getURL();
- }
- }
- getState().selectedItemIcon = selectedItemIcon;
- }
-
- @Override
- public void attach() {
- super.attach();
-
- // Update icon for ConnectorResource
- updateSelectedItemIcon(getValue());
- }
-
- @Override
- protected Element writeItem(Element design, T item, DesignContext context) {
- Element element = design.appendElement("option");
-
- String caption = getItemCaptionGenerator().apply(item);
- if (caption != null) {
- element.html(DesignFormatter.encodeForTextNode(caption));
- } else {
- element.html(DesignFormatter.encodeForTextNode(item.toString()));
- }
- element.attr("item", item.toString());
-
- Resource icon = getItemIconGenerator().apply(item);
- if (icon != null) {
- DesignAttributeHandler.writeAttribute("icon", element.attributes(),
- icon, null, Resource.class, context);
- }
-
- String style = getStyleGenerator().apply(item);
- if (style != null) {
- element.attr("style", style);
- }
-
- if (isSelected(item)) {
- element.attr("selected", true);
- }
-
- return element;
- }
-
- @Override
- protected void readItems(Element design, DesignContext context) {
- setStyleGenerator(new DeclarativeStyleGenerator<>(getStyleGenerator()));
- super.readItems(design, context);
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- @Override
- protected T readItem(Element child, Set<T> selected,
- DesignContext context) {
- T item = super.readItem(child, selected, context);
-
- if (child.hasAttr("style")) {
- StyleGenerator<T> styleGenerator = getStyleGenerator();
- if (styleGenerator instanceof DeclarativeStyleGenerator) {
- ((DeclarativeStyleGenerator) styleGenerator).setStyle(item,
- child.attr("style"));
- } else {
- throw new IllegalStateException(String.format("Don't know how "
- + "to set style using current style generator '%s'",
- styleGenerator.getClass().getName()));
- }
- }
- return item;
- }
-
- @Override
- public DataProvider<T, ?> getDataProvider() {
- return internalGetDataProvider();
- }
-
- @Override
- public <C> void setDataProvider(DataProvider<T, C> dataProvider,
- SerializableFunction<String, C> filterConverter) {
- Objects.requireNonNull(dataProvider, "dataProvider cannot be null");
- Objects.requireNonNull(filterConverter,
- "filterConverter cannot be null");
-
- SerializableFunction<String, C> convertOrNull = filterText -> {
- if (filterText == null || filterText.isEmpty()) {
- return null;
- }
-
- return filterConverter.apply(filterText);
- };
-
- SerializableConsumer<C> providerFilterSlot = internalSetDataProvider(
- dataProvider,
- convertOrNull.apply(getState(false).currentFilterText));
-
- filterSlot = filter -> providerFilterSlot
- .accept(convertOrNull.apply(filter));
- }
-
- /**
- * Sets a CallbackDataProvider using the given fetch items callback and a
- * size callback.
- * <p>
- * This method is a shorthand for making a {@link CallbackDataProvider} that
- * handles a partial {@link com.vaadin.data.provider.Query Query} object.
- *
- * @param fetchItems
- * a callback for fetching items
- * @param sizeCallback
- * a callback for getting the count of items
- *
- * @see CallbackDataProvider
- * @see #setDataProvider(DataProvider)
- */
- public void setDataProvider(FetchItemsCallback<T> fetchItems,
- SerializableToIntFunction<String> sizeCallback) {
- setDataProvider(new CallbackDataProvider<>(
- q -> fetchItems.fetchItems(q.getFilter().orElse(""),
- q.getOffset(), q.getLimit()),
- q -> sizeCallback.applyAsInt(q.getFilter().orElse(""))));
- }
-
- /**
- * Predicate to check {@link ComboBox} item captions against user typed
- * strings.
- *
- * @see ComboBox#setItems(CaptionFilter, Collection)
- * @see ComboBox#setItems(CaptionFilter, Object[])
- * @since 8.0
- */
- @FunctionalInterface
- public interface CaptionFilter
- extends SerializableBiPredicate<String, String> {
-
- /**
- * Check item caption against entered text.
- *
- * @param itemCaption
- * the caption of the item to filter, not {@code null}
- * @param filterText
- * user entered filter, not {@code null}
- * @return {@code true} if item passes the filter and should be listed,
- * {@code false} otherwise
- */
- @Override
- public boolean test(String itemCaption, String filterText);
- }
-
- private static Logger getLogger() {
- return Logger.getLogger(ComboBox.class.getName());
- }
- }
|