Fixes vaadin/framework8-issues#423 Change-Id: Ifd252b8feed323708a7ae73af2b836832570d192tags/8.0.0.alpha7
@@ -17,7 +17,6 @@ package com.vaadin.data; | |||
import java.io.Serializable; | |||
import java.util.Collection; | |||
import java.util.Set; | |||
import com.vaadin.server.data.DataSource; | |||
@@ -28,12 +27,9 @@ import com.vaadin.server.data.DataSource; | |||
* | |||
* @param <T> | |||
* the item data type | |||
* @param <SELECTIONMODEL> | |||
* the selection logic supported by this listing | |||
* @since 8.0 | |||
*/ | |||
public interface Listing<T, SELECTIONMODEL extends SelectionModel<T>> | |||
extends Serializable { | |||
public interface Listing<T> extends Serializable { | |||
/** | |||
* Returns the source of data items used by this listing. | |||
@@ -51,13 +47,6 @@ public interface Listing<T, SELECTIONMODEL extends SelectionModel<T>> | |||
*/ | |||
void setDataSource(DataSource<T> dataSource); | |||
/** | |||
* Returns the selection model for this listing. | |||
* | |||
* @return the selection model, not null | |||
*/ | |||
SELECTIONMODEL getSelectionModel(); | |||
/** | |||
* Sets the collection of data items of this listing. | |||
* | |||
@@ -79,54 +68,4 @@ public interface Listing<T, SELECTIONMODEL extends SelectionModel<T>> | |||
setDataSource(DataSource.create(items)); | |||
} | |||
/* SelectionModel helper methods */ | |||
/** | |||
* Returns an immutable set of the currently selected items. The iteration | |||
* order of the items in the returned set is specified by the | |||
* {@linkplain #getSelectionModel() selection model} used. | |||
* | |||
* @return the current selection | |||
* | |||
* @see SelectionModel#getSelectedItems | |||
*/ | |||
default Set<T> getSelectedItems() { | |||
return getSelectionModel().getSelectedItems(); | |||
} | |||
/** | |||
* Selects the given item. If the item is already selected, does nothing. | |||
* | |||
* @param item | |||
* the item to select, not null | |||
* | |||
* @see SelectionModel#select | |||
*/ | |||
default void select(T item) { | |||
getSelectionModel().select(item); | |||
} | |||
/** | |||
* Deselects the given item. If the item is not currently selected, does | |||
* nothing. | |||
* | |||
* @param item | |||
* the item to deselect, not null | |||
* | |||
* @see SelectionModel#deselect | |||
*/ | |||
default void deselect(T item) { | |||
getSelectionModel().deselect(item); | |||
} | |||
/** | |||
* Returns whether the given item is currently selected. | |||
* | |||
* @param item | |||
* the item to check, not null | |||
* @return {@code true} if the item is selected, {@code false} otherwise | |||
*/ | |||
default boolean isSelected(T item) { | |||
return getSelectionModel().isSelected(item); | |||
} | |||
} |
@@ -67,8 +67,8 @@ public class SingleSelectionChangeEvent<T> extends ValueChangeEvent<T> | |||
@Override | |||
@SuppressWarnings("unchecked") | |||
public AbstractListing<T, ?> getComponent() { | |||
return (AbstractListing<T, ?>) super.getComponent(); | |||
public AbstractListing<T> getComponent() { | |||
return (AbstractListing<T>) super.getComponent(); | |||
} | |||
@Override |
@@ -32,13 +32,11 @@ import com.vaadin.server.data.DataSource; | |||
* | |||
* @param <T> | |||
* the item data type | |||
* @param <SELECTIONMODEL> | |||
* the selection logic supported by this listing | |||
* | |||
* @since 8.0 | |||
*/ | |||
public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T>> | |||
extends AbstractComponent implements Listing<T, SELECTIONMODEL> { | |||
public abstract class AbstractListing<T> extends AbstractComponent | |||
implements Listing<T> { | |||
/** | |||
* A helper base class for creating extensions for Listing components. This | |||
@@ -57,7 +55,7 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T | |||
* @param listing | |||
* the parent component to add to | |||
*/ | |||
public void extend(AbstractListing<T, ?> listing) { | |||
public void extend(AbstractListing<T> listing) { | |||
super.extend(listing); | |||
listing.addDataGenerator(this); | |||
} | |||
@@ -82,8 +80,8 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T | |||
@Override | |||
@SuppressWarnings("unchecked") | |||
public AbstractListing<T, ?> getParent() { | |||
return (AbstractListing<T, ?>) super.getParent(); | |||
public AbstractListing<T> getParent() { | |||
return (AbstractListing<T>) super.getParent(); | |||
} | |||
/** | |||
@@ -100,8 +98,6 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T | |||
private final DataCommunicator<T> dataCommunicator; | |||
private SELECTIONMODEL selectionModel; | |||
/** | |||
* Creates a new {@code AbstractListing} with a default data communicator. | |||
* <p> | |||
@@ -146,29 +142,6 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T | |||
return getDataCommunicator().getDataSource(); | |||
} | |||
@Override | |||
public SELECTIONMODEL getSelectionModel() { | |||
assert selectionModel != null : "No selection model set by " | |||
+ getClass().getName() + " constructor"; | |||
return selectionModel; | |||
} | |||
/** | |||
* Sets the selection model for this listing. | |||
* | |||
* @param model | |||
* the selection model to use, not null | |||
*/ | |||
protected void setSelectionModel(SELECTIONMODEL model) { | |||
if (selectionModel != null) { | |||
throw new IllegalStateException( | |||
"A selection model can't be changed."); | |||
} | |||
Objects.requireNonNull(model, "selection model cannot be null"); | |||
selectionModel = model; | |||
} | |||
/** | |||
* Adds the given data generator to this listing. If the generator was | |||
* already added, does nothing. |
@@ -52,158 +52,16 @@ import elemental.json.JsonObject; | |||
* @author Vaadin Ltd | |||
* @since 8.0 | |||
*/ | |||
public abstract class AbstractMultiSelect<T> | |||
extends AbstractListing<T, Multi<T>> implements HasValue<Set<T>> { | |||
public abstract class AbstractMultiSelect<T> extends AbstractListing<T> | |||
implements MultiSelect<T> { | |||
/** | |||
* Simple implementation of multiselectmodel. | |||
*/ | |||
protected class SimpleMultiSelectModel implements SelectionModel.Multi<T> { | |||
private Set<T> selection = new LinkedHashSet<>(); | |||
@Override | |||
public void select(T item) { | |||
// Not user originated | |||
select(item, false); | |||
} | |||
/** | |||
* Selects the given item. Depending on the implementation, may cause | |||
* other items to be deselected. If the item is already selected, does | |||
* nothing. | |||
* | |||
* @param item | |||
* the item to select, not null | |||
* @param userOriginated | |||
* {@code true} if this was used originated, {@code false} if | |||
* not | |||
*/ | |||
protected void select(T item, boolean userOriginated) { | |||
if (selection.contains(item)) { | |||
return; | |||
} | |||
updateSelection(set -> set.add(item), userOriginated); | |||
} | |||
@Override | |||
public void updateSelection(Set<T> addedItems, Set<T> removedItems) { | |||
updateSelection(addedItems, removedItems, false); | |||
} | |||
/** | |||
* Updates the selection by adding and removing the given items. | |||
* | |||
* @param addedItems | |||
* the items added to selection, not {@code} null | |||
* @param removedItems | |||
* the items removed from selection, not {@code} null | |||
* @param userOriginated | |||
* {@code true} if this was used originated, {@code false} if | |||
* not | |||
*/ | |||
protected void updateSelection(Set<T> addedItems, Set<T> removedItems, | |||
boolean userOriginated) { | |||
Objects.requireNonNull(addedItems); | |||
Objects.requireNonNull(removedItems); | |||
// if there are duplicates, some item is both added & removed, just | |||
// discard that and leave things as was before | |||
addedItems.removeIf(item -> removedItems.remove(item)); | |||
if (selection.containsAll(addedItems) | |||
&& Collections.disjoint(selection, removedItems)) { | |||
return; | |||
} | |||
updateSelection(set -> { | |||
// order of add / remove does not matter since no duplicates | |||
set.removeAll(removedItems); | |||
set.addAll(addedItems); | |||
}, userOriginated); | |||
} | |||
@Override | |||
public Set<T> getSelectedItems() { | |||
return Collections.unmodifiableSet(new LinkedHashSet<>(selection)); | |||
} | |||
@Override | |||
public void deselect(T item) { | |||
// Not user originated | |||
deselect(item, false); | |||
} | |||
/** | |||
* Deselects the given item. If the item is not currently selected, does | |||
* nothing. | |||
* | |||
* @param item | |||
* the item to deselect, not null | |||
* @param userOriginated | |||
* {@code true} if this was used originated, {@code false} if | |||
* not | |||
*/ | |||
protected void deselect(T item, boolean userOriginated) { | |||
if (!selection.contains(item)) { | |||
return; | |||
} | |||
updateSelection(set -> set.remove(item), userOriginated); | |||
} | |||
/** | |||
* Removes the given items. Any item that is not currently selected, is | |||
* ignored. If none of the items are selected, does nothing. | |||
* | |||
* @param items | |||
* the items to deselect, not {@code null} | |||
* @param userOriginated | |||
* {@code true} if this was used originated, {@code false} if | |||
* not | |||
*/ | |||
protected void deselect(Set<T> items, boolean userOriginated) { | |||
Objects.requireNonNull(items); | |||
if (items.stream().noneMatch(i -> isSelected(i))) { | |||
return; | |||
} | |||
updateSelection(set -> set.removeAll(items), userOriginated); | |||
} | |||
@Override | |||
public void deselectAll() { | |||
if (selection.isEmpty()) { | |||
return; | |||
} | |||
updateSelection(Set::clear, false); | |||
} | |||
private void updateSelection(Consumer<Set<T>> handler, | |||
boolean userOriginated) { | |||
LinkedHashSet<T> oldSelection = new LinkedHashSet<>(selection); | |||
handler.accept(selection); | |||
LinkedHashSet<T> newSelection = new LinkedHashSet<>(selection); | |||
fireEvent(new MultiSelectionEvent<>(AbstractMultiSelect.this, | |||
oldSelection, userOriginated)); | |||
getDataCommunicator().reset(); | |||
} | |||
@Override | |||
public boolean isSelected(T item) { | |||
return selection.contains(item); | |||
} | |||
} | |||
private Set<T> selection = new LinkedHashSet<>(); | |||
private class MultiSelectServerRpcImpl implements MultiSelectServerRpc { | |||
@Override | |||
public void updateSelection(Set<String> selectedItemKeys, | |||
Set<String> deselectedItemKeys) { | |||
getSelectionModel().updateSelection( | |||
AbstractMultiSelect.this.updateSelection( | |||
getItemsForSelectionChange(selectedItemKeys), | |||
getItemsForSelectionChange(deselectedItemKeys), true); | |||
} | |||
@@ -223,10 +81,6 @@ public abstract class AbstractMultiSelect<T> | |||
return Optional.of(item); | |||
} | |||
private SimpleMultiSelectModel getSelectionModel() { | |||
return (SimpleMultiSelectModel) AbstractMultiSelect.this | |||
.getSelectionModel(); | |||
} | |||
} | |||
private class MultiSelectDataGenerator implements DataGenerator<T> { | |||
@@ -250,7 +104,7 @@ public abstract class AbstractMultiSelect<T> | |||
true); | |||
} | |||
if (getSelectionModel().isSelected(data)) { | |||
if (isSelected(data)) { | |||
jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED, | |||
true); | |||
} | |||
@@ -287,8 +141,6 @@ public abstract class AbstractMultiSelect<T> | |||
* Creates a new multi select with an empty data source. | |||
*/ | |||
protected AbstractMultiSelect() { | |||
setSelectionModel(new SimpleMultiSelectModel()); | |||
registerRpc(new MultiSelectServerRpcImpl()); | |||
// #FIXME it should be the responsibility of the SelectionModel | |||
@@ -304,6 +156,7 @@ public abstract class AbstractMultiSelect<T> | |||
* the value change listener, not {@code null} | |||
* @return a registration for the listener | |||
*/ | |||
@Override | |||
public Registration addSelectionListener( | |||
MultiSelectionListener<T> listener) { | |||
addListener(MultiSelectionEvent.class, listener, | |||
@@ -376,8 +229,7 @@ public abstract class AbstractMultiSelect<T> | |||
Set<T> copy = value.stream().map(Objects::requireNonNull) | |||
.collect(Collectors.toCollection(LinkedHashSet::new)); | |||
getSelectionModel().updateSelection(copy, | |||
new LinkedHashSet<>(getSelectionModel().getSelectedItems())); | |||
updateSelection(copy, new LinkedHashSet<>(getSelectedItems())); | |||
} | |||
@Override | |||
@@ -502,4 +354,122 @@ public abstract class AbstractMultiSelect<T> | |||
public boolean isReadOnly() { | |||
return super.isReadOnly(); | |||
} | |||
@Override | |||
public void updateSelection(Set<T> addedItems, Set<T> removedItems) { | |||
updateSelection(addedItems, removedItems, false); | |||
} | |||
/** | |||
* Updates the selection by adding and removing the given items. | |||
* | |||
* @param addedItems | |||
* the items added to selection, not {@code} null | |||
* @param removedItems | |||
* the items removed from selection, not {@code} null | |||
* @param userOriginated | |||
* {@code true} if this was used originated, {@code false} if not | |||
*/ | |||
protected void updateSelection(Set<T> addedItems, Set<T> removedItems, | |||
boolean userOriginated) { | |||
Objects.requireNonNull(addedItems); | |||
Objects.requireNonNull(removedItems); | |||
// if there are duplicates, some item is both added & removed, just | |||
// discard that and leave things as was before | |||
addedItems.removeIf(item -> removedItems.remove(item)); | |||
if (selection.containsAll(addedItems) | |||
&& Collections.disjoint(selection, removedItems)) { | |||
return; | |||
} | |||
updateSelection(set -> { | |||
// order of add / remove does not matter since no duplicates | |||
set.removeAll(removedItems); | |||
set.addAll(addedItems); | |||
}, userOriginated); | |||
} | |||
@Override | |||
public Set<T> getSelectedItems() { | |||
return Collections.unmodifiableSet(new LinkedHashSet<>(selection)); | |||
} | |||
@Override | |||
public void deselectAll() { | |||
if (selection.isEmpty()) { | |||
return; | |||
} | |||
updateSelection(Set::clear, false); | |||
} | |||
@Override | |||
public boolean isSelected(T item) { | |||
return selection.contains(item); | |||
} | |||
/** | |||
* Deselects the given item. If the item is not currently selected, does | |||
* nothing. | |||
* | |||
* @param item | |||
* the item to deselect, not null | |||
* @param userOriginated | |||
* {@code true} if this was used originated, {@code false} if not | |||
*/ | |||
protected void deselect(T item, boolean userOriginated) { | |||
if (!selection.contains(item)) { | |||
return; | |||
} | |||
updateSelection(set -> set.remove(item), userOriginated); | |||
} | |||
/** | |||
* Removes the given items. Any item that is not currently selected, is | |||
* ignored. If none of the items are selected, does nothing. | |||
* | |||
* @param items | |||
* the items to deselect, not {@code null} | |||
* @param userOriginated | |||
* {@code true} if this was used originated, {@code false} if not | |||
*/ | |||
protected void deselect(Set<T> items, boolean userOriginated) { | |||
Objects.requireNonNull(items); | |||
if (items.stream().noneMatch(i -> isSelected(i))) { | |||
return; | |||
} | |||
updateSelection(set -> set.removeAll(items), userOriginated); | |||
} | |||
/** | |||
* Selects the given item. Depending on the implementation, may cause other | |||
* items to be deselected. If the item is already selected, does nothing. | |||
* | |||
* @param item | |||
* the item to select, not null | |||
* @param userOriginated | |||
* {@code true} if this was used originated, {@code false} if not | |||
*/ | |||
protected void select(T item, boolean userOriginated) { | |||
if (selection.contains(item)) { | |||
return; | |||
} | |||
updateSelection(set -> set.add(item), userOriginated); | |||
} | |||
private void updateSelection(Consumer<Set<T>> handler, | |||
boolean userOriginated) { | |||
LinkedHashSet<T> oldSelection = new LinkedHashSet<>(selection); | |||
handler.accept(selection); | |||
fireEvent(new MultiSelectionEvent<>(AbstractMultiSelect.this, | |||
oldSelection, userOriginated)); | |||
getDataCommunicator().reset(); | |||
} | |||
} |
@@ -43,189 +43,9 @@ import com.vaadin.util.ReflectTools; | |||
* | |||
* @since 8.0 | |||
*/ | |||
public abstract class AbstractSingleSelect<T> extends | |||
AbstractListing<T, AbstractSingleSelect<T>.AbstractSingleSelection> | |||
public abstract class AbstractSingleSelect<T> extends AbstractListing<T> | |||
implements SingleSelect<T> { | |||
/** | |||
* A base class for single selection model implementations. Listens to | |||
* {@code SelectionServerRpc} invocations to track selection requests by the | |||
* client. Maintaining the selection state and communicating it from the | |||
* server to the client is the responsibility of the implementing class. | |||
*/ | |||
public abstract class AbstractSingleSelection | |||
implements SelectionModel.Single<T> { | |||
/** | |||
* Creates a new {@code SimpleSingleSelection} instance. | |||
*/ | |||
public AbstractSingleSelection() { | |||
registerRpc(new SelectionServerRpc() { | |||
@Override | |||
public void select(String key) { | |||
setSelectedFromClient(key); | |||
} | |||
@Override | |||
public void deselect(String key) { | |||
if (isKeySelected(key)) { | |||
setSelectedFromClient(null); | |||
} | |||
} | |||
}); | |||
} | |||
@Override | |||
public void deselect(T item) { | |||
Objects.requireNonNull(item, "deselected item cannot be null"); | |||
if (isSelected(item)) { | |||
setSelectedFromServer(null); | |||
} | |||
} | |||
@Override | |||
public void select(T item) { | |||
Objects.requireNonNull(item, "selected item cannot be null"); | |||
setSelectedFromServer(item); | |||
} | |||
/** | |||
* Returns the communication key of the selected item or {@code null} if | |||
* no item is selected. | |||
* | |||
* @return the key of the selected item if any, {@code null} otherwise. | |||
*/ | |||
protected abstract String getSelectedKey(); | |||
/** | |||
* Sets the selected item based on the given communication key. If the | |||
* key is {@code null}, clears the current selection if any. | |||
* | |||
* @param key | |||
* the key of the selected item or {@code null} to clear | |||
* selection | |||
*/ | |||
protected abstract void doSetSelectedKey(String key); | |||
/** | |||
* Sets the selection based on a client request. Does nothing if the | |||
* select component is {@linkplain Component#isReadOnly()} or if the | |||
* selection would not change. Otherwise updates the selection and fires | |||
* a selection change event with {@code isUserOriginated == true}. | |||
* | |||
* @param key | |||
* the key of the item to select or {@code null} to clear | |||
* selection | |||
*/ | |||
protected void setSelectedFromClient(String key) { | |||
if (isReadOnly()) { | |||
return; | |||
} | |||
if (isKeySelected(key)) { | |||
return; | |||
} | |||
doSetSelectedKey(key); | |||
fireEvent(new SingleSelectionChangeEvent<>( | |||
AbstractSingleSelect.this, true)); | |||
} | |||
/** | |||
* Sets the selection based on server API call. Does nothing if the | |||
* selection would not change; otherwise updates the selection and fires | |||
* a selection change event with {@code isUserOriginated == false}. | |||
* | |||
* @param item | |||
* the item to select or {@code null} to clear selection | |||
*/ | |||
protected void setSelectedFromServer(T item) { | |||
// TODO creates a key if item not in data source | |||
String key = itemToKey(item); | |||
if (isKeySelected(key) || isSelected(item)) { | |||
return; | |||
} | |||
doSetSelectedKey(key); | |||
fireEvent(new SingleSelectionChangeEvent<>( | |||
AbstractSingleSelect.this, false)); | |||
} | |||
/** | |||
* Returns whether the given key maps to the currently selected item. | |||
* | |||
* @param key | |||
* the key to test or {@code null} to test whether nothing is | |||
* selected | |||
* @return {@code true} if the key equals the key of the currently | |||
* selected item (or {@code null} if no selection), | |||
* {@code false} otherwise. | |||
*/ | |||
protected boolean isKeySelected(String key) { | |||
return Objects.equals(key, getSelectedKey()); | |||
} | |||
/** | |||
* Returns the communication key assigned to the given item. | |||
* | |||
* @param item | |||
* the item whose key to return | |||
* @return the assigned key | |||
*/ | |||
protected String itemToKey(T item) { | |||
if (item == null) { | |||
return null; | |||
} else { | |||
// TODO creates a key if item not in data source | |||
return getDataCommunicator().getKeyMapper().key(item); | |||
} | |||
} | |||
/** | |||
* Returns the item that the given key is assigned to, or {@code null} | |||
* if there is no such item. | |||
* | |||
* @param key | |||
* the key whose item to return | |||
* @return the associated item if any, {@code null} otherwise. | |||
*/ | |||
protected T keyToItem(String key) { | |||
return getDataCommunicator().getKeyMapper().get(key); | |||
} | |||
} | |||
/** | |||
* A simple single selection model using the {@code AbstractSingleSelect} | |||
* RPC and state to communicate with the client. Has no client-side | |||
* counterpart; the listing connector is expected to handle selection. | |||
* Client-to-server selection is passed via {@link SelectionServerRpc} and | |||
* server-to-client via {@link AbstractSingleSelectState#selectedItemKey}. | |||
*/ | |||
protected class SimpleSingleSelection extends AbstractSingleSelection { | |||
/** | |||
* Creates a new {@code SimpleSingleSelection}. | |||
*/ | |||
public SimpleSingleSelection() { | |||
} | |||
@Override | |||
public Optional<T> getSelectedItem() { | |||
return Optional.ofNullable(keyToItem(getSelectedKey())); | |||
} | |||
@Override | |||
protected String getSelectedKey() { | |||
return getState(false).selectedItemKey; | |||
} | |||
@Override | |||
protected void doSetSelectedKey(String key) { | |||
getState().selectedItemKey = key; | |||
} | |||
} | |||
@Deprecated | |||
private static final Method SELECTION_CHANGE_METHOD = ReflectTools | |||
.findMethod(SingleSelectionListener.class, "accept", | |||
@@ -240,6 +60,7 @@ public abstract class AbstractSingleSelect<T> extends | |||
* {@link AbstractSingleSelect.AbstractSingleSelection} . | |||
*/ | |||
protected AbstractSingleSelect() { | |||
init(); | |||
} | |||
/** | |||
@@ -260,6 +81,7 @@ public abstract class AbstractSingleSelect<T> extends | |||
*/ | |||
protected AbstractSingleSelect(DataCommunicator<T> dataCommunicator) { | |||
super(dataCommunicator); | |||
init(); | |||
} | |||
/** | |||
@@ -285,7 +107,7 @@ public abstract class AbstractSingleSelect<T> extends | |||
* otherwise | |||
*/ | |||
public Optional<T> getSelectedItem() { | |||
return getSelectionModel().getSelectedItem(); | |||
return Optional.ofNullable(keyToItem(getSelectedKey())); | |||
} | |||
/** | |||
@@ -296,7 +118,7 @@ public abstract class AbstractSingleSelect<T> extends | |||
* the item to select or {@code null} to clear selection | |||
*/ | |||
public void setSelectedItem(T item) { | |||
getSelectionModel().setSelectedItem(item); | |||
setSelectedFromServer(item); | |||
} | |||
/** | |||
@@ -369,4 +191,152 @@ public abstract class AbstractSingleSelect<T> extends | |||
public boolean isReadOnly() { | |||
return super.isReadOnly(); | |||
} | |||
public void deselect(T item) { | |||
Objects.requireNonNull(item, "deselected item cannot be null"); | |||
if (isSelected(item)) { | |||
setSelectedFromServer(null); | |||
} | |||
} | |||
public void select(T item) { | |||
Objects.requireNonNull(item, "selected item cannot be null"); | |||
setSelectedFromServer(item); | |||
} | |||
/** | |||
* Returns the communication key of the selected item or {@code null} if no | |||
* item is selected. | |||
* | |||
* @return the key of the selected item if any, {@code null} otherwise. | |||
*/ | |||
protected String getSelectedKey() { | |||
return getState(false).selectedItemKey; | |||
} | |||
/** | |||
* Sets the selected item based on the given communication key. If the key | |||
* is {@code null}, clears the current selection if any. | |||
* | |||
* @param key | |||
* the key of the selected item or {@code null} to clear | |||
* selection | |||
*/ | |||
protected void doSetSelectedKey(String key) { | |||
getState().selectedItemKey = key; | |||
} | |||
/** | |||
* Sets the selection based on a client request. Does nothing if the select | |||
* component is {@linkplain Component#isReadOnly()} or if the selection | |||
* would not change. Otherwise updates the selection and fires a selection | |||
* change event with {@code isUserOriginated == true}. | |||
* | |||
* @param key | |||
* the key of the item to select or {@code null} to clear | |||
* selection | |||
*/ | |||
protected void setSelectedFromClient(String key) { | |||
if (isReadOnly()) { | |||
return; | |||
} | |||
if (isKeySelected(key)) { | |||
return; | |||
} | |||
doSetSelectedKey(key); | |||
fireEvent(new SingleSelectionChangeEvent<>(AbstractSingleSelect.this, | |||
true)); | |||
} | |||
/** | |||
* Sets the selection based on server API call. Does nothing if the | |||
* selection would not change; otherwise updates the selection and fires a | |||
* selection change event with {@code isUserOriginated == false}. | |||
* | |||
* @param item | |||
* the item to select or {@code null} to clear selection | |||
*/ | |||
protected void setSelectedFromServer(T item) { | |||
// TODO creates a key if item not in data source | |||
String key = itemToKey(item); | |||
if (isKeySelected(key) || isSelected(item)) { | |||
return; | |||
} | |||
doSetSelectedKey(key); | |||
fireEvent(new SingleSelectionChangeEvent<>(AbstractSingleSelect.this, | |||
false)); | |||
} | |||
/** | |||
* Returns whether the given key maps to the currently selected item. | |||
* | |||
* @param key | |||
* the key to test or {@code null} to test whether nothing is | |||
* selected | |||
* @return {@code true} if the key equals the key of the currently selected | |||
* item (or {@code null} if no selection), {@code false} otherwise. | |||
*/ | |||
protected boolean isKeySelected(String key) { | |||
return Objects.equals(key, getSelectedKey()); | |||
} | |||
/** | |||
* Returns the communication key assigned to the given item. | |||
* | |||
* @param item | |||
* the item whose key to return | |||
* @return the assigned key | |||
*/ | |||
protected String itemToKey(T item) { | |||
if (item == null) { | |||
return null; | |||
} else { | |||
// TODO creates a key if item not in data source | |||
return getDataCommunicator().getKeyMapper().key(item); | |||
} | |||
} | |||
/** | |||
* Returns the item that the given key is assigned to, or {@code null} if | |||
* there is no such item. | |||
* | |||
* @param key | |||
* the key whose item to return | |||
* @return the associated item if any, {@code null} otherwise. | |||
*/ | |||
protected T keyToItem(String key) { | |||
return getDataCommunicator().getKeyMapper().get(key); | |||
} | |||
/** | |||
* Returns whether the given item is currently selected. | |||
* | |||
* @param item | |||
* the item to check, not null | |||
* @return {@code true} if the item is selected, {@code false} otherwise | |||
*/ | |||
public boolean isSelected(T item) { | |||
return Objects.equals(getValue(), item); | |||
} | |||
private void init() { | |||
registerRpc(new SelectionServerRpc() { | |||
@Override | |||
public void select(String key) { | |||
setSelectedFromClient(key); | |||
} | |||
@Override | |||
public void deselect(String key) { | |||
if (isKeySelected(key)) { | |||
setSelectedFromClient(null); | |||
} | |||
} | |||
}); | |||
} | |||
} |
@@ -55,24 +55,6 @@ import elemental.json.JsonObject; | |||
public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>, | |||
FieldEvents.BlurNotifier, FieldEvents.FocusNotifier { | |||
/** | |||
* Custom single selection model for ComboBox. | |||
*/ | |||
protected class ComboBoxSelectionModel extends SimpleSingleSelection { | |||
@Override | |||
protected void doSetSelectedKey(String key) { | |||
super.doSetSelectedKey(key); | |||
String selectedCaption = null; | |||
T value = getDataCommunicator().getKeyMapper().get(key); | |||
if (value != null) { | |||
selectedCaption = getItemCaptionGenerator().apply(value); | |||
} | |||
getState().selectedItemCaption = selectedCaption; | |||
} | |||
} | |||
/** | |||
* Handler that adds a new item based on user input when the new items | |||
* allowed mode is active. | |||
@@ -158,7 +140,6 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>, | |||
}; | |||
} | |||
}); | |||
setSelectionModel(new ComboBoxSelectionModel()); | |||
init(); | |||
} | |||
@@ -540,24 +521,6 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>, | |||
this.filter = filter; | |||
} | |||
/** | |||
* Sets the value of this object. If the new value is not equal to | |||
* {@code getValue()}, fires a {@link ValueChangeEvent}. | |||
* | |||
* @param value | |||
* the new value, may be {@code null} | |||
*/ | |||
@Override | |||
public void setValue(T value) { | |||
getSelectionModel().setSelectedFromServer(value); | |||
} | |||
@Override | |||
public T getValue() { | |||
return getSelectionModel().getSelectedItem().orElse(null); | |||
} | |||
@Override | |||
public Registration addValueChangeListener( | |||
HasValue.ValueChangeListener<T> listener) { | |||
@@ -577,4 +540,16 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>, | |||
return (ComboBoxState) super.getState(markAsDirty); | |||
} | |||
@Override | |||
protected void doSetSelectedKey(String key) { | |||
super.doSetSelectedKey(key); | |||
String selectedCaption = null; | |||
T value = getDataCommunicator().getKeyMapper().get(key); | |||
if (value != null) { | |||
selectedCaption = getItemCaptionGenerator().apply(value); | |||
} | |||
getState().selectedItemCaption = selectedCaption; | |||
} | |||
} |
@@ -37,9 +37,11 @@ import java.util.function.Function; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.Stream; | |||
import com.vaadin.data.SelectionModel.Single; | |||
import com.vaadin.event.ConnectorEvent; | |||
import com.vaadin.event.ContextClickEvent; | |||
import com.vaadin.event.EventListener; | |||
import com.vaadin.event.selection.SingleSelectionChangeEvent; | |||
import com.vaadin.server.EncodeResult; | |||
import com.vaadin.server.JsonCodec; | |||
import com.vaadin.server.SerializableComparator; | |||
@@ -48,6 +50,7 @@ import com.vaadin.server.data.SortOrder; | |||
import com.vaadin.shared.MouseEventDetails; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.data.DataCommunicatorConstants; | |||
import com.vaadin.shared.data.selection.SelectionServerRpc; | |||
import com.vaadin.shared.data.sort.SortDirection; | |||
import com.vaadin.shared.ui.grid.ColumnState; | |||
import com.vaadin.shared.ui.grid.GridConstants; | |||
@@ -488,7 +491,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { | |||
extends AbstractListingExtension<T> { | |||
@Override | |||
public void extend(AbstractListing<T, ?> grid) { | |||
public void extend(AbstractListing<T> grid) { | |||
if (!(grid instanceof Grid)) { | |||
throw new IllegalArgumentException( | |||
getClass().getSimpleName() + " can only extend Grid"); | |||
@@ -1485,7 +1488,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { | |||
} | |||
} | |||
private final class SingleSelection extends AbstractSingleSelection { | |||
private final class SingleSelection implements Single<T> { | |||
private T selectedItem = null; | |||
SingleSelection() { | |||
@@ -1494,6 +1497,20 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { | |||
json.put(DataCommunicatorConstants.SELECTED, true); | |||
} | |||
}); | |||
registerRpc(new SelectionServerRpc() { | |||
@Override | |||
public void select(String key) { | |||
setSelectedFromClient(key); | |||
} | |||
@Override | |||
public void deselect(String key) { | |||
if (isKeySelected(key)) { | |||
setSelectedFromClient(null); | |||
} | |||
} | |||
}); | |||
} | |||
@Override | |||
@@ -1502,16 +1519,51 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { | |||
} | |||
@Override | |||
public void deselect(T item) { | |||
Objects.requireNonNull(item, "deselected item cannot be null"); | |||
if (isSelected(item)) { | |||
setSelectedFromServer(null); | |||
} | |||
} | |||
@Override | |||
public void select(T item) { | |||
Objects.requireNonNull(item, "selected item cannot be null"); | |||
setSelectedFromServer(item); | |||
} | |||
/** | |||
* Returns whether the given key maps to the currently selected item. | |||
* | |||
* @param key | |||
* the key to test or {@code null} to test whether nothing is | |||
* selected | |||
* @return {@code true} if the key equals the key of the currently | |||
* selected item (or {@code null} if no selection), | |||
* {@code false} otherwise. | |||
*/ | |||
protected boolean isKeySelected(String key) { | |||
return Objects.equals(key, getSelectedKey()); | |||
} | |||
@Override | |||
/** | |||
* Returns the communication key of the selected item or {@code null} if | |||
* no item is selected. | |||
* | |||
* @return the key of the selected item if any, {@code null} otherwise. | |||
*/ | |||
protected String getSelectedKey() { | |||
return itemToKey(selectedItem); | |||
} | |||
@Override | |||
/** | |||
* Sets the selected item based on the given communication key. If the | |||
* key is {@code null}, clears the current selection if any. | |||
* | |||
* @param key | |||
* the key of the selected item or {@code null} to clear | |||
* selection | |||
*/ | |||
protected void doSetSelectedKey(String key) { | |||
if (selectedItem != null) { | |||
getDataCommunicator().refresh(selectedItem); | |||
@@ -1521,6 +1573,76 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { | |||
getDataCommunicator().refresh(selectedItem); | |||
} | |||
} | |||
/** | |||
* Sets the selection based on a client request. Does nothing if the | |||
* select component is {@linkplain Component#isReadOnly()} or if the | |||
* selection would not change. Otherwise updates the selection and fires | |||
* a selection change event with {@code isUserOriginated == true}. | |||
* | |||
* @param key | |||
* the key of the item to select or {@code null} to clear | |||
* selection | |||
*/ | |||
protected void setSelectedFromClient(String key) { | |||
if (isReadOnly()) { | |||
return; | |||
} | |||
if (isKeySelected(key)) { | |||
return; | |||
} | |||
doSetSelectedKey(key); | |||
fireEvent(new SingleSelectionChangeEvent<>(Grid.this, true)); | |||
} | |||
/** | |||
* Sets the selection based on server API call. Does nothing if the | |||
* selection would not change; otherwise updates the selection and fires | |||
* a selection change event with {@code isUserOriginated == false}. | |||
* | |||
* @param item | |||
* the item to select or {@code null} to clear selection | |||
*/ | |||
protected void setSelectedFromServer(T item) { | |||
// TODO creates a key if item not in data source | |||
String key = itemToKey(item); | |||
if (isKeySelected(key) || isSelected(item)) { | |||
return; | |||
} | |||
doSetSelectedKey(key); | |||
fireEvent(new SingleSelectionChangeEvent<>(Grid.this, false)); | |||
} | |||
/** | |||
* Returns the communication key assigned to the given item. | |||
* | |||
* @param item | |||
* the item whose key to return | |||
* @return the assigned key | |||
*/ | |||
protected String itemToKey(T item) { | |||
if (item == null) { | |||
return null; | |||
} else { | |||
// TODO creates a key if item not in data source | |||
return getDataCommunicator().getKeyMapper().key(item); | |||
} | |||
} | |||
/** | |||
* Returns the item that the given key is assigned to, or {@code null} | |||
* if there is no such item. | |||
* | |||
* @param key | |||
* the key whose item to return | |||
* @return the associated item if any, {@code null} otherwise. | |||
*/ | |||
protected T keyToItem(String key) { | |||
return getDataCommunicator().getKeyMapper().get(key); | |||
} | |||
} | |||
/** | |||
@@ -1713,6 +1835,8 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { | |||
private int counter = 0; | |||
private Single<T> selectionModel; | |||
/** | |||
* Constructor for the {@link Grid} component. | |||
*/ | |||
@@ -2561,6 +2685,38 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { | |||
fireColumnReorderEvent(false); | |||
} | |||
/** | |||
* Returns the selection model for this listing. | |||
* | |||
* @return the selection model, not null | |||
*/ | |||
public Single<T> getSelectionModel() { | |||
assert selectionModel != null : "No selection model set by " | |||
+ getClass().getName() + " constructor"; | |||
return selectionModel; | |||
} | |||
@Override | |||
public Optional<T> getSelectedItem() { | |||
return getSelectionModel().getSelectedItem(); | |||
} | |||
/** | |||
* Sets the selection model for this listing. | |||
* | |||
* @param model | |||
* the selection model to use, not null | |||
*/ | |||
protected void setSelectionModel(Single<T> model) { | |||
if (selectionModel != null) { | |||
throw new IllegalStateException( | |||
"A selection model can't be changed."); | |||
} | |||
Objects.requireNonNull(model, "selection model cannot be null"); | |||
selectionModel = model; | |||
} | |||
@Override | |||
protected GridState getState() { | |||
return getState(true); |
@@ -0,0 +1,139 @@ | |||
/* | |||
* Copyright 2000-2016 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.Collections; | |||
import java.util.LinkedHashSet; | |||
import java.util.Objects; | |||
import java.util.Set; | |||
import java.util.stream.Stream; | |||
import com.vaadin.data.HasValue; | |||
import com.vaadin.event.selection.MultiSelectionListener; | |||
import com.vaadin.shared.Registration; | |||
/** | |||
* Multi selection component which allows to select and deselect multiple items. | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
* @param <T> | |||
* the type of the items to select | |||
* | |||
* @since 8.0 | |||
* | |||
*/ | |||
public interface MultiSelect<T> extends HasValue<Set<T>>, Serializable { | |||
/** | |||
* Adds the given items to the set of currently selected items. | |||
* <p> | |||
* By default this does not clear any previous selection. To do that, use | |||
* {@link #deselectAll()}. | |||
* <p> | |||
* If the all the items were already selected, this is a NO-OP. | |||
* <p> | |||
* This is a short-hand for {@link #updateSelection(Set, Set)} with nothing | |||
* to deselect. | |||
* | |||
* @param items | |||
* to add to selection, not {@code null} | |||
*/ | |||
public default void select(T... items) { | |||
Objects.requireNonNull(items); | |||
Stream.of(items).forEach(Objects::requireNonNull); | |||
updateSelection(new LinkedHashSet<>(Arrays.asList(items)), | |||
Collections.emptySet()); | |||
} | |||
/** | |||
* Removes the given items from the set of currently selected items. | |||
* <p> | |||
* If the none of the items were selected, this is a NO-OP. | |||
* <p> | |||
* This is a short-hand for {@link #updateSelection(Set, Set)} with nothing | |||
* to select. | |||
* | |||
* @param items | |||
* to remove from selection, not {@code null} | |||
*/ | |||
public default void deselect(T... items) { | |||
Objects.requireNonNull(items); | |||
Stream.of(items).forEach(Objects::requireNonNull); | |||
updateSelection(Collections.emptySet(), | |||
new LinkedHashSet<>(Arrays.asList(items))); | |||
} | |||
/** | |||
* Updates the selection by adding and removing the given items from it. | |||
* <p> | |||
* If all the added items were already selected and the removed items were | |||
* not selected, this is a NO-OP. | |||
* <p> | |||
* Duplicate items (in both add & remove sets) are ignored. | |||
* | |||
* @param addedItems | |||
* the items to add, not {@code null} | |||
* @param removedItems | |||
* the items to remove, not {@code null} | |||
*/ | |||
public void updateSelection(Set<T> addedItems, Set<T> removedItems); | |||
/** | |||
* Returns an immutable set of the currently selected items. It is safe to | |||
* invoke other {@code SelectionModel} methods while iterating over the set. | |||
* <p> | |||
* <em>Implementation note:</em> the iteration order of the items in the | |||
* returned set should be well-defined and documented by the implementing | |||
* class. | |||
* | |||
* @return the items in the current selection, not null | |||
*/ | |||
public Set<T> getSelectedItems(); | |||
/** | |||
* Deselects all currently selected items. | |||
*/ | |||
public default void deselectAll() { | |||
getSelectedItems().forEach(this::deselect); | |||
} | |||
/** | |||
* Returns whether the given item is currently selected. | |||
* | |||
* @param item | |||
* the item to check, not null | |||
* @return {@code true} if the item is selected, {@code false} otherwise | |||
*/ | |||
public default boolean isSelected(T item) { | |||
return getSelectedItems().contains(item); | |||
} | |||
/** | |||
* Adds a selection listener that will be called when the selection is | |||
* changed either by the user or programmatically. | |||
* | |||
* @param listener | |||
* the value change listener, not {@code null} | |||
* @return a registration for the listener | |||
*/ | |||
public Registration addSelectionListener( | |||
MultiSelectionListener<T> listener); | |||
} |
@@ -52,8 +52,6 @@ public class NativeSelect<T> extends AbstractSingleSelect<T> | |||
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); | |||
addDataGenerator((item, json) -> json | |||
.put(DataCommunicatorConstants.DATA, String.valueOf(item))); | |||
setSelectionModel(new SimpleSingleSelection()); | |||
} | |||
/** |
@@ -103,7 +103,6 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T> | |||
*/ | |||
public RadioButtonGroup() { | |||
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); | |||
setSelectionModel(new SimpleSingleSelection()); | |||
addDataGenerator(new DataGenerator<T>() { | |||
@Override | |||
@@ -127,7 +126,7 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T> | |||
true); | |||
} | |||
if (getSelectionModel().isSelected(data)) { | |||
if (isSelected(data)) { | |||
jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED, | |||
true); | |||
} |
@@ -157,7 +157,7 @@ public class BinderMultiSelectTest | |||
converterBinder.readBean(new AtomicReference<>("TWO")); | |||
assertEquals(Collections.singleton(TestEnum.TWO), | |||
select.getSelectionModel().getSelectedItems()); | |||
select.getSelectedItems()); | |||
} | |||
@Test |
@@ -1,5 +1,7 @@ | |||
package com.vaadin.tests.components.grid; | |||
import java.util.Optional; | |||
import org.junit.Assert; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
@@ -18,17 +20,17 @@ public class GridSelectionTest { | |||
@Test | |||
public void testGridWithSingleSelection() { | |||
Assert.assertFalse(grid.isSelected("Foo")); | |||
grid.select("Foo"); | |||
Assert.assertTrue(grid.isSelected("Foo")); | |||
Assert.assertEquals(1, grid.getSelectedItems().size()); | |||
Assert.assertEquals("Foo", grid.getSelectedItems().iterator().next()); | |||
grid.select("Bar"); | |||
Assert.assertFalse(grid.isSelected("Foo")); | |||
Assert.assertTrue(grid.isSelected("Bar")); | |||
grid.deselect("Bar"); | |||
Assert.assertFalse(grid.isSelected("Bar")); | |||
Assert.assertEquals(0, grid.getSelectedItems().size()); | |||
Assert.assertFalse(grid.getSelectionModel().isSelected("Foo")); | |||
grid.getSelectionModel().select("Foo"); | |||
Assert.assertTrue(grid.getSelectionModel().isSelected("Foo")); | |||
Assert.assertEquals(Optional.of("Foo"), grid.getSelectedItem()); | |||
grid.getSelectionModel().select("Bar"); | |||
Assert.assertFalse(grid.getSelectionModel().isSelected("Foo")); | |||
Assert.assertTrue(grid.getSelectionModel().isSelected("Bar")); | |||
grid.getSelectionModel().deselect("Bar"); | |||
Assert.assertFalse(grid.getSelectionModel().isSelected("Bar")); | |||
Assert.assertFalse( | |||
grid.getSelectionModel().getSelectedItem().isPresent()); | |||
} | |||
} |
@@ -22,10 +22,6 @@ public class AbstractListingTest { | |||
private final class TestListing extends AbstractSingleSelect<String> { | |||
protected TestListing() { | |||
setSelectionModel(new SimpleSingleSelection()); | |||
} | |||
/** | |||
* Used to execute data generation | |||
*/ |
@@ -37,7 +37,6 @@ import org.junit.runners.Parameterized.Parameters; | |||
import org.mockito.Mockito; | |||
import com.vaadin.data.HasValue.ValueChangeEvent; | |||
import com.vaadin.data.SelectionModel.Multi; | |||
import com.vaadin.event.selection.MultiSelectionEvent; | |||
import com.vaadin.event.selection.MultiSelectionListener; | |||
import com.vaadin.server.data.DataSource; | |||
@@ -56,18 +55,16 @@ public class AbstractMultiSelectTest { | |||
@Parameter | |||
public AbstractMultiSelect<String> selectToTest; | |||
private Multi<String> selectionModel; | |||
private MultiSelectServerRpc rpc; | |||
private Registration registration; | |||
@Before | |||
public void setUp() { | |||
selectToTest.getSelectionModel().deselectAll(); | |||
selectToTest.deselectAll(); | |||
// Intentional deviation from upcoming selection order | |||
selectToTest.setDataSource( | |||
DataSource.create("3", "2", "1", "5", "8", "7", "4", "6")); | |||
selectionModel = selectToTest.getSelectionModel(); | |||
rpc = ComponentTest.getRpcProxy(selectToTest, | |||
MultiSelectServerRpc.class); | |||
} | |||
@@ -82,28 +79,28 @@ public class AbstractMultiSelectTest { | |||
@Test | |||
public void stableSelectionOrder() { | |||
selectionModel.select("1"); | |||
selectionModel.select("2"); | |||
selectionModel.select("3"); | |||
selectToTest.select("1"); | |||
selectToTest.select("2"); | |||
selectToTest.select("3"); | |||
assertSelectionOrder(selectionModel, "1", "2", "3"); | |||
assertSelectionOrder("1", "2", "3"); | |||
selectionModel.deselect("1"); | |||
assertSelectionOrder(selectionModel, "2", "3"); | |||
selectToTest.deselect("1"); | |||
assertSelectionOrder("2", "3"); | |||
selectionModel.select("1"); | |||
assertSelectionOrder(selectionModel, "2", "3", "1"); | |||
selectToTest.select("1"); | |||
assertSelectionOrder("2", "3", "1"); | |||
selectionModel.selectItems("7", "8", "4"); | |||
assertSelectionOrder(selectionModel, "2", "3", "1", "7", "8", "4"); | |||
selectToTest.select("7", "8", "4"); | |||
assertSelectionOrder("2", "3", "1", "7", "8", "4"); | |||
selectionModel.deselectItems("2", "1", "4", "5"); | |||
assertSelectionOrder(selectionModel, "3", "7", "8"); | |||
selectToTest.deselect("2", "1", "4", "5"); | |||
assertSelectionOrder("3", "7", "8"); | |||
selectionModel.updateSelection( | |||
selectToTest.updateSelection( | |||
new LinkedHashSet<>(Arrays.asList("5", "2")), | |||
new LinkedHashSet<>(Arrays.asList("3", "8"))); | |||
assertSelectionOrder(selectionModel, "7", "5", "2"); | |||
assertSelectionOrder("7", "5", "2"); | |||
} | |||
@Test | |||
@@ -120,27 +117,27 @@ public class AbstractMultiSelectTest { | |||
selectToTest.select("2"); | |||
selectToTest.deselect("2"); | |||
selectToTest.getSelectionModel().deselectAll(); | |||
selectToTest.deselectAll(); | |||
selectToTest.getSelectionModel().selectItems("2", "3", "4"); | |||
selectToTest.getSelectionModel().deselectItems("1", "4"); | |||
selectToTest.select("2", "3", "4"); | |||
selectToTest.deselect("1", "4"); | |||
Assert.assertEquals(6, listenerCount.get()); | |||
// select partly selected | |||
selectToTest.getSelectionModel().selectItems("2", "3", "4"); | |||
selectToTest.select("2", "3", "4"); | |||
Assert.assertEquals(7, listenerCount.get()); | |||
// select completely selected | |||
selectToTest.getSelectionModel().selectItems("2", "3", "4"); | |||
selectToTest.select("2", "3", "4"); | |||
Assert.assertEquals(7, listenerCount.get()); | |||
// deselect partly not selected | |||
selectToTest.getSelectionModel().selectItems("1", "4"); | |||
selectToTest.select("1", "4"); | |||
Assert.assertEquals(8, listenerCount.get()); | |||
// deselect completely not selected | |||
selectToTest.getSelectionModel().selectItems("1", "4"); | |||
selectToTest.select("1", "4"); | |||
Assert.assertEquals(8, listenerCount.get()); | |||
} | |||
@@ -154,88 +151,88 @@ public class AbstractMultiSelectTest { | |||
}); | |||
rpcSelect("1"); | |||
assertSelectionOrder(selectionModel, "1"); | |||
assertSelectionOrder("1"); | |||
rpcSelect("2"); | |||
assertSelectionOrder(selectionModel, "1", "2"); | |||
rpcDeselect("2"); | |||
assertSelectionOrder(selectionModel, "1"); | |||
assertSelectionOrder("1", "2"); | |||
rpcDeselectItems("2"); | |||
assertSelectionOrder("1"); | |||
rpcSelect("3", "6"); | |||
assertSelectionOrder(selectionModel, "1", "3", "6"); | |||
rpcDeselect("1", "3"); | |||
assertSelectionOrder(selectionModel, "6"); | |||
assertSelectionOrder("1", "3", "6"); | |||
rpcDeselectItems("1", "3"); | |||
assertSelectionOrder("6"); | |||
Assert.assertEquals(5, listenerCount.get()); | |||
// select partly selected | |||
rpcSelect("2", "3", "4"); | |||
Assert.assertEquals(6, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "2", "3", "4"); | |||
assertSelectionOrder("6", "2", "3", "4"); | |||
// select completely selected | |||
rpcSelect("2", "3", "4"); | |||
Assert.assertEquals(6, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "2", "3", "4"); | |||
assertSelectionOrder("6", "2", "3", "4"); | |||
// deselect partly not selected | |||
rpcDeselect("1", "4"); | |||
rpcDeselectItems("1", "4"); | |||
Assert.assertEquals(7, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "2", "3"); | |||
assertSelectionOrder("6", "2", "3"); | |||
// deselect completely not selected | |||
rpcDeselect("1", "4"); | |||
rpcDeselectItems("1", "4"); | |||
Assert.assertEquals(7, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "2", "3"); | |||
assertSelectionOrder("6", "2", "3"); | |||
// select completely selected and deselect completely not selected | |||
rpcUpdateSelection(new String[] { "3" }, new String[] { "1", "4" }); | |||
Assert.assertEquals(7, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "2", "3"); | |||
assertSelectionOrder("6", "2", "3"); | |||
// select partly selected and deselect completely not selected | |||
rpcUpdateSelection(new String[] { "4", "2" }, | |||
new String[] { "1", "8" }); | |||
Assert.assertEquals(8, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "2", "3", "4"); | |||
assertSelectionOrder("6", "2", "3", "4"); | |||
// select completely selected and deselect partly not selected | |||
rpcUpdateSelection(new String[] { "4", "3" }, | |||
new String[] { "1", "2" }); | |||
Assert.assertEquals(9, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "3", "4"); | |||
assertSelectionOrder("6", "3", "4"); | |||
// duplicate case - ignored | |||
rpcUpdateSelection(new String[] { "2" }, new String[] { "2" }); | |||
Assert.assertEquals(9, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "3", "4"); | |||
assertSelectionOrder("6", "3", "4"); | |||
// duplicate case - duplicate removed | |||
rpcUpdateSelection(new String[] { "2" }, new String[] { "2", "3" }); | |||
Assert.assertEquals(10, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "4"); | |||
assertSelectionOrder("6", "4"); | |||
// duplicate case - duplicate removed | |||
rpcUpdateSelection(new String[] { "6", "8" }, new String[] { "6" }); | |||
Assert.assertEquals(11, listenerCount.get()); | |||
assertSelectionOrder(selectionModel, "6", "4", "8"); | |||
assertSelectionOrder("6", "4", "8"); | |||
} | |||
@Test | |||
public void getValue() { | |||
selectionModel.selectItems("1"); | |||
selectToTest.select("1"); | |||
Assert.assertEquals(Collections.singleton("1"), | |||
selectToTest.getValue()); | |||
selectionModel.deselectAll(); | |||
selectToTest.deselectAll(); | |||
LinkedHashSet<String> set = new LinkedHashSet<>(); | |||
set.add("1"); | |||
set.add("5"); | |||
selectionModel.selectItems(set.toArray(new String[2])); | |||
selectToTest.select(set.toArray(new String[2])); | |||
Assert.assertEquals(set, selectToTest.getValue()); | |||
set.add("3"); | |||
selectionModel.selectItems("3"); | |||
selectToTest.select("3"); | |||
Assert.assertEquals(set, selectToTest.getValue()); | |||
} | |||
@@ -259,26 +256,21 @@ public class AbstractMultiSelectTest { | |||
selectToTest.setValue(Collections.singleton("1")); | |||
Assert.assertEquals(Collections.singleton("1"), | |||
selectionModel.getSelectedItems()); | |||
selectToTest.getSelectedItems()); | |||
Set<String> set = new LinkedHashSet<>(); | |||
set.add("4"); | |||
set.add("3"); | |||
selectToTest.setValue(set); | |||
Assert.assertEquals(set, selectionModel.getSelectedItems()); | |||
Assert.assertEquals(set, selectToTest.getSelectedItems()); | |||
} | |||
@Test | |||
@SuppressWarnings({ "unchecked", "rawtypes", "serial" }) | |||
public void setValue_isDelegatedToDeselectAndUpdateSelection() { | |||
Multi<?> model = Mockito.mock(Multi.class); | |||
AbstractMultiSelect<String> select = new AbstractMultiSelect<String>() { | |||
@Override | |||
public Multi<String> getSelectionModel() { | |||
return (Multi<String>) model; | |||
} | |||
}; | |||
AbstractMultiSelect<String> select = Mockito | |||
.mock(AbstractMultiSelect.class); | |||
Set set = new LinkedHashSet<>(); | |||
set.add("foo1"); | |||
@@ -287,11 +279,12 @@ public class AbstractMultiSelectTest { | |||
selected.add("bar1"); | |||
selected.add("bar"); | |||
selected.add("bar2"); | |||
Mockito.when(model.getSelectedItems()).thenReturn(selected); | |||
Mockito.when(select.getSelectedItems()).thenReturn(selected); | |||
Mockito.doCallRealMethod().when(select).setValue(Mockito.anySet()); | |||
select.setValue(set); | |||
Mockito.verify(model).updateSelection(set, selected); | |||
Mockito.verify(select).updateSelection(set, selected); | |||
} | |||
@SuppressWarnings({ "unchecked", "serial" }) | |||
@@ -336,7 +329,7 @@ public class AbstractMultiSelectTest { | |||
rpcUpdateSelection(keysToSelect, new String[] {}); | |||
} | |||
private void rpcDeselect(String... keysToDeselect) { | |||
private void rpcDeselectItems(String... keysToDeselect) { | |||
rpcUpdateSelection(new String[] {}, keysToDeselect); | |||
} | |||
@@ -353,9 +346,8 @@ public class AbstractMultiSelectTest { | |||
.key(dataObject); | |||
} | |||
private static void assertSelectionOrder(Multi<String> selectionModel, | |||
String... selectionOrder) { | |||
private void assertSelectionOrder(String... selectionOrder) { | |||
Assert.assertEquals(Arrays.asList(selectionOrder), | |||
new ArrayList<>(selectionModel.getSelectedItems())); | |||
new ArrayList<>(selectToTest.getSelectedItems())); | |||
} | |||
} |
@@ -21,7 +21,6 @@ import static org.junit.Assert.assertTrue; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Optional; | |||
@@ -34,13 +33,11 @@ import org.junit.Test; | |||
import org.mockito.Mockito; | |||
import com.vaadin.data.HasValue.ValueChangeEvent; | |||
import com.vaadin.data.SelectionModel.Multi; | |||
import com.vaadin.event.selection.SingleSelectionChangeEvent; | |||
import com.vaadin.event.selection.SingleSelectionListener; | |||
import com.vaadin.server.data.datasource.bov.Person; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.data.DataCommunicatorClientRpc; | |||
import com.vaadin.ui.AbstractSingleSelect.AbstractSingleSelection; | |||
/** | |||
* Test for {@link AbstractSingleSelect} and {@link AbstractSingleSelection} | |||
@@ -49,20 +46,15 @@ import com.vaadin.ui.AbstractSingleSelect.AbstractSingleSelection; | |||
*/ | |||
public class AbstractSingleSelectTest { | |||
private PersonListing.AbstractSingleSelection selectionModel; | |||
private List<Person> selectionChanges; | |||
private static class PersonListing extends AbstractSingleSelect<Person> { | |||
public PersonListing() { | |||
setSelectionModel(new SimpleSingleSelection()); | |||
} | |||
} | |||
@Before | |||
public void initListing() { | |||
listing = new PersonListing(); | |||
listing.setItems(PERSON_A, PERSON_B, PERSON_C); | |||
selectionModel = listing.getSelectionModel(); | |||
selectionChanges = new ArrayList<>(); | |||
listing.addSelectionListener(e -> selectionChanges.add(e.getValue())); | |||
} | |||
@@ -77,18 +69,17 @@ public class AbstractSingleSelectTest { | |||
@Test | |||
public void select() { | |||
selectionModel.select(PERSON_B); | |||
listing.select(PERSON_B); | |||
assertTrue(selectionModel.getSelectedItem().isPresent()); | |||
assertTrue(listing.getSelectedItem().isPresent()); | |||
assertEquals(PERSON_B, selectionModel.getSelectedItem().orElse(null)); | |||
assertEquals(PERSON_B, listing.getSelectedItem().orElse(null)); | |||
assertFalse(selectionModel.isSelected(PERSON_A)); | |||
assertTrue(selectionModel.isSelected(PERSON_B)); | |||
assertFalse(selectionModel.isSelected(PERSON_C)); | |||
assertFalse(listing.isSelected(PERSON_A)); | |||
assertTrue(listing.isSelected(PERSON_B)); | |||
assertFalse(listing.isSelected(PERSON_C)); | |||
assertEquals(Collections.singleton(PERSON_B), | |||
selectionModel.getSelectedItems()); | |||
assertEquals(Optional.of(PERSON_B), listing.getSelectedItem()); | |||
assertEquals(Arrays.asList(PERSON_B), selectionChanges); | |||
} | |||
@@ -96,16 +87,16 @@ public class AbstractSingleSelectTest { | |||
@Test | |||
public void selectDeselect() { | |||
selectionModel.select(PERSON_B); | |||
selectionModel.deselect(PERSON_B); | |||
listing.select(PERSON_B); | |||
listing.deselect(PERSON_B); | |||
assertFalse(selectionModel.getSelectedItem().isPresent()); | |||
assertFalse(listing.getSelectedItem().isPresent()); | |||
assertFalse(selectionModel.isSelected(PERSON_A)); | |||
assertFalse(selectionModel.isSelected(PERSON_B)); | |||
assertFalse(selectionModel.isSelected(PERSON_C)); | |||
assertFalse(listing.isSelected(PERSON_A)); | |||
assertFalse(listing.isSelected(PERSON_B)); | |||
assertFalse(listing.isSelected(PERSON_C)); | |||
assertTrue(selectionModel.getSelectedItems().isEmpty()); | |||
assertFalse(listing.getSelectedItem().isPresent()); | |||
assertEquals(Arrays.asList(PERSON_B, null), selectionChanges); | |||
} | |||
@@ -113,17 +104,16 @@ public class AbstractSingleSelectTest { | |||
@Test | |||
public void reselect() { | |||
selectionModel.select(PERSON_B); | |||
selectionModel.select(PERSON_C); | |||
listing.select(PERSON_B); | |||
listing.select(PERSON_C); | |||
assertEquals(PERSON_C, selectionModel.getSelectedItem().orElse(null)); | |||
assertEquals(PERSON_C, listing.getSelectedItem().orElse(null)); | |||
assertFalse(selectionModel.isSelected(PERSON_A)); | |||
assertFalse(selectionModel.isSelected(PERSON_B)); | |||
assertTrue(selectionModel.isSelected(PERSON_C)); | |||
assertFalse(listing.isSelected(PERSON_A)); | |||
assertFalse(listing.isSelected(PERSON_B)); | |||
assertTrue(listing.isSelected(PERSON_C)); | |||
assertEquals(Collections.singleton(PERSON_C), | |||
selectionModel.getSelectedItems()); | |||
assertEquals(Optional.of(PERSON_C), listing.getSelectedItem()); | |||
assertEquals(Arrays.asList(PERSON_B, PERSON_C), selectionChanges); | |||
} | |||
@@ -131,17 +121,16 @@ public class AbstractSingleSelectTest { | |||
@Test | |||
public void deselectNoOp() { | |||
selectionModel.select(PERSON_C); | |||
selectionModel.deselect(PERSON_B); | |||
listing.select(PERSON_C); | |||
listing.deselect(PERSON_B); | |||
assertEquals(PERSON_C, selectionModel.getSelectedItem().orElse(null)); | |||
assertEquals(PERSON_C, listing.getSelectedItem().orElse(null)); | |||
assertFalse(selectionModel.isSelected(PERSON_A)); | |||
assertFalse(selectionModel.isSelected(PERSON_B)); | |||
assertTrue(selectionModel.isSelected(PERSON_C)); | |||
assertFalse(listing.isSelected(PERSON_A)); | |||
assertFalse(listing.isSelected(PERSON_B)); | |||
assertTrue(listing.isSelected(PERSON_C)); | |||
assertEquals(Collections.singleton(PERSON_C), | |||
selectionModel.getSelectedItems()); | |||
assertEquals(Optional.of(PERSON_C), listing.getSelectedItem()); | |||
assertEquals(Arrays.asList(PERSON_C), selectionChanges); | |||
} | |||
@@ -149,17 +138,16 @@ public class AbstractSingleSelectTest { | |||
@Test | |||
public void selectTwice() { | |||
selectionModel.select(PERSON_C); | |||
selectionModel.select(PERSON_C); | |||
listing.select(PERSON_C); | |||
listing.select(PERSON_C); | |||
assertEquals(PERSON_C, selectionModel.getSelectedItem().orElse(null)); | |||
assertEquals(PERSON_C, listing.getSelectedItem().orElse(null)); | |||
assertFalse(selectionModel.isSelected(PERSON_A)); | |||
assertFalse(selectionModel.isSelected(PERSON_B)); | |||
assertTrue(selectionModel.isSelected(PERSON_C)); | |||
assertFalse(listing.isSelected(PERSON_A)); | |||
assertFalse(listing.isSelected(PERSON_B)); | |||
assertTrue(listing.isSelected(PERSON_C)); | |||
assertEquals(Collections.singleton(PERSON_C), | |||
selectionModel.getSelectedItems()); | |||
assertEquals(Optional.of(PERSON_C), listing.getSelectedItem()); | |||
assertEquals(Arrays.asList(PERSON_C), selectionChanges); | |||
} | |||
@@ -167,28 +155,28 @@ public class AbstractSingleSelectTest { | |||
@Test | |||
public void deselectTwice() { | |||
selectionModel.select(PERSON_C); | |||
selectionModel.deselect(PERSON_C); | |||
selectionModel.deselect(PERSON_C); | |||
listing.select(PERSON_C); | |||
listing.deselect(PERSON_C); | |||
listing.deselect(PERSON_C); | |||
assertFalse(selectionModel.getSelectedItem().isPresent()); | |||
assertFalse(listing.getSelectedItem().isPresent()); | |||
assertFalse(selectionModel.isSelected(PERSON_A)); | |||
assertFalse(selectionModel.isSelected(PERSON_B)); | |||
assertFalse(selectionModel.isSelected(PERSON_C)); | |||
assertFalse(listing.isSelected(PERSON_A)); | |||
assertFalse(listing.isSelected(PERSON_B)); | |||
assertFalse(listing.isSelected(PERSON_C)); | |||
assertTrue(selectionModel.getSelectedItems().isEmpty()); | |||
assertFalse(listing.getSelectedItem().isPresent()); | |||
assertEquals(Arrays.asList(PERSON_C, null), selectionChanges); | |||
} | |||
@Test | |||
public void getValue() { | |||
selectionModel.setSelectedItem(PERSON_B); | |||
listing.setSelectedItem(PERSON_B); | |||
Assert.assertEquals(PERSON_B, listing.getValue()); | |||
selectionModel.deselectAll(); | |||
listing.deselect(PERSON_B); | |||
Assert.assertNull(listing.getValue()); | |||
} | |||
@@ -211,11 +199,11 @@ public class AbstractSingleSelectTest { | |||
public void setValue() { | |||
listing.setValue(PERSON_C); | |||
Assert.assertEquals(PERSON_C, selectionModel.getSelectedItem().get()); | |||
Assert.assertEquals(PERSON_C, listing.getSelectedItem().get()); | |||
listing.setValue(null); | |||
Assert.assertFalse(selectionModel.getSelectedItem().isPresent()); | |||
Assert.assertFalse(listing.getSelectedItem().isPresent()); | |||
} | |||
@Test | |||
@@ -268,15 +256,10 @@ public class AbstractSingleSelectTest { | |||
} | |||
@Test | |||
@SuppressWarnings({ "unchecked", "rawtypes", "serial" }) | |||
@SuppressWarnings({ "unchecked", "rawtypes" }) | |||
public void setValue_isDelegatedToDeselectAndUpdateSelection() { | |||
Multi<?> model = Mockito.mock(Multi.class); | |||
AbstractMultiSelect<String> select = new AbstractMultiSelect<String>() { | |||
@Override | |||
public Multi<String> getSelectionModel() { | |||
return (Multi<String>) model; | |||
} | |||
}; | |||
AbstractMultiSelect<String> select = Mockito | |||
.mock(AbstractMultiSelect.class); | |||
Set set = new LinkedHashSet<>(); | |||
set.add("foo1"); | |||
@@ -285,11 +268,12 @@ public class AbstractSingleSelectTest { | |||
selected.add("bar1"); | |||
selected.add("bar"); | |||
selected.add("bar2"); | |||
Mockito.when(model.getSelectedItems()).thenReturn(selected); | |||
Mockito.when(select.getSelectedItems()).thenReturn(selected); | |||
Mockito.doCallRealMethod().when(select).setValue(Mockito.anySet()); | |||
select.setValue(set); | |||
Mockito.verify(model).updateSelection(set, selected); | |||
Mockito.verify(select).updateSelection(set, selected); | |||
} | |||
} |
@@ -23,14 +23,12 @@ import org.junit.Assert; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import com.vaadin.data.SelectionModel; | |||
import com.vaadin.data.SelectionModel.Multi; | |||
import com.vaadin.server.data.DataSource; | |||
import com.vaadin.shared.data.selection.SelectionServerRpc; | |||
public class RadioButtonGroupTest { | |||
private RadioButtonGroup<String> radioButtonGroup; | |||
private SelectionModel.Single<String> selectionModel; | |||
@Before | |||
public void setUp() { | |||
@@ -38,7 +36,6 @@ public class RadioButtonGroupTest { | |||
// Intentional deviation from upcoming selection order | |||
radioButtonGroup | |||
.setDataSource(DataSource.create("Third", "Second", "First")); | |||
selectionModel = radioButtonGroup.getSelectionModel(); | |||
} | |||
@Test | |||
@@ -54,7 +51,7 @@ public class RadioButtonGroupTest { | |||
radioButtonGroup.select("Second"); | |||
radioButtonGroup.deselect("Second"); | |||
radioButtonGroup.getSelectionModel().deselectAll(); | |||
radioButtonGroup.deselect("Second"); | |||
Assert.assertEquals(3, listenerCount.get()); | |||
} |
@@ -21,7 +21,6 @@ import java.util.stream.Collectors; | |||
import java.util.stream.IntStream; | |||
import com.googlecode.gentyref.GenericTypeReflector; | |||
import com.vaadin.data.SelectionModel; | |||
import com.vaadin.event.FieldEvents.BlurNotifier; | |||
import com.vaadin.event.FieldEvents.FocusNotifier; | |||
import com.vaadin.server.VaadinRequest; | |||
@@ -31,7 +30,7 @@ import com.vaadin.ui.AbstractListing; | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public abstract class AbstractListingFocusBlurTest<T extends AbstractListing<Integer, S> & FocusNotifier & BlurNotifier, S extends SelectionModel<Integer>> | |||
public abstract class AbstractListingFocusBlurTest<T extends AbstractListing<Integer> & FocusNotifier & BlurNotifier> | |||
extends AbstractTestUIWithLog { | |||
@Override | |||
@@ -45,7 +44,7 @@ public abstract class AbstractListingFocusBlurTest<T extends AbstractListing<Int | |||
if (valueType instanceof Class<?>) { | |||
Class<?> clazz = (Class<?>) valueType; | |||
try { | |||
AbstractListing<Integer, ?> select = (AbstractListing<Integer, ?>) clazz | |||
AbstractListing<Integer> select = (AbstractListing<Integer>) clazz | |||
.newInstance(); | |||
select.setItems( | |||
IntStream.range(1, 10).mapToObj(Integer::valueOf) |
@@ -8,7 +8,7 @@ import com.vaadin.tests.components.AbstractComponentTest; | |||
import com.vaadin.ui.AbstractListing; | |||
@Widgetset("com.vaadin.DefaultWidgetSet") | |||
public abstract class AbstractListingTestUI<T extends AbstractListing<Object, ?>> | |||
public abstract class AbstractListingTestUI<T extends AbstractListing<Object>> | |||
extends AbstractComponentTest<T> { | |||
@Override |
@@ -5,7 +5,6 @@ import java.util.List; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.IntStream; | |||
import com.vaadin.data.SelectionModel.Multi; | |||
import com.vaadin.ui.AbstractMultiSelect; | |||
import com.vaadin.ui.ItemCaptionGenerator; | |||
@@ -38,10 +37,8 @@ public abstract class AbstractMultiSelectTestUI<MULTISELECT extends AbstractMult | |||
} | |||
protected void createSelectionMenu() { | |||
createClickAction( | |||
"Clear selection", selectionCategory, (component, item, | |||
data) -> component.getSelectionModel().deselectAll(), | |||
""); | |||
createClickAction("Clear selection", selectionCategory, | |||
(component, item, data) -> component.deselectAll(), ""); | |||
Command<MULTISELECT, String> toggleSelection = (component, item, | |||
data) -> toggleSelection(item); | |||
@@ -59,20 +56,18 @@ public abstract class AbstractMultiSelectTestUI<MULTISELECT extends AbstractMult | |||
} | |||
private void toggleSelection(String item) { | |||
Multi<Object> selectionModel = getComponent().getSelectionModel(); | |||
if (selectionModel.isSelected(item)) { | |||
selectionModel.deselect(item); | |||
if (getComponent().isSelected(item)) { | |||
getComponent().deselect(item); | |||
} else { | |||
selectionModel.select(item); | |||
getComponent().select(item); | |||
} | |||
} | |||
private void toggleMultiSelection(boolean add, List<String> items) { | |||
Multi<Object> selectionModel = getComponent().getSelectionModel(); | |||
if (add) { | |||
selectionModel.selectItems(items.toArray()); | |||
getComponent().select(items.toArray()); | |||
} else { | |||
selectionModel.deselectItems(items.toArray()); | |||
getComponent().deselect(items.toArray()); | |||
} | |||
} | |||
@@ -45,11 +45,12 @@ public abstract class AbstractSingleSelectTestUI<T extends AbstractSingleSelect< | |||
options.put("Item 100", "Item 100"); | |||
createSelectAction("Select", "Selection", options, "None", | |||
(c, selected, data) -> { | |||
(component, selected, data) -> { | |||
if (selected != null) { | |||
c.select(selected); | |||
component.select(selected); | |||
} else { | |||
c.getSelectedItems().forEach(c::deselect); | |||
component.getSelectedItem() | |||
.ifPresent(component::deselect); | |||
} | |||
}); | |||
} |
@@ -15,7 +15,6 @@ | |||
*/ | |||
package com.vaadin.tests.components.checkboxgroup; | |||
import com.vaadin.data.SelectionModel.Multi; | |||
import com.vaadin.tests.components.AbstractListingFocusBlurTest; | |||
import com.vaadin.ui.CheckBoxGroup; | |||
@@ -28,7 +27,7 @@ import com.vaadin.ui.CheckBoxGroup; | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class CheckBoxGroupFocusBlur extends | |||
AbstractListingFocusBlurTest<CheckBoxGroup<Integer>, Multi<Integer>> { | |||
public class CheckBoxGroupFocusBlur | |||
extends AbstractListingFocusBlurTest<CheckBoxGroup<Integer>> { | |||
} |
@@ -233,11 +233,9 @@ public class GridBasics extends AbstractTestUIWithLog { | |||
MenuItem headerTypeMenu = columnMenu.addItem("Header Type", null); | |||
headerTypeMenu.addItem("Text Header", selectedItem -> grid | |||
.getDefaultHeaderRow().getCell(col).setText("Text Header")); | |||
headerTypeMenu | |||
.addItem("HTML Header", | |||
selectedItem -> grid.getDefaultHeaderRow() | |||
.getCell(col) | |||
.setHtml("<b>HTML Header</b>")); | |||
headerTypeMenu.addItem("HTML Header", | |||
selectedItem -> grid.getDefaultHeaderRow().getCell(col) | |||
.setHtml("<b>HTML Header</b>")); | |||
headerTypeMenu.addItem("Widget Header", selectedItem -> { | |||
final Button button = new Button("Button Header"); | |||
button.addClickListener(clickEvent -> log("Button clicked!")); | |||
@@ -293,15 +291,12 @@ public class GridBasics extends AbstractTestUIWithLog { | |||
: null)) | |||
.setCheckable(true); | |||
stateMenu | |||
.addItem("Cell description generator", | |||
item -> grid.getColumns().stream().findFirst() | |||
.ifPresent( | |||
c -> c.setDescriptionGenerator( | |||
item.isChecked() | |||
? t -> "Cell tooltip for row " | |||
+ t.getRowNumber() | |||
+ ", Column 0" | |||
: null))) | |||
.addItem("Cell description generator", item -> grid.getColumns() | |||
.stream().findFirst() | |||
.ifPresent(c -> c.setDescriptionGenerator( | |||
item.isChecked() ? t -> "Cell tooltip for row " | |||
+ t.getRowNumber() + ", Column 0" | |||
: null))) | |||
.setCheckable(true); | |||
stateMenu.addItem("Item click listener", new Command() { | |||
@@ -393,10 +388,10 @@ public class GridBasics extends AbstractTestUIWithLog { | |||
private void createBodyMenu(MenuItem rowMenu) { | |||
rowMenu.addItem("Toggle first row selection", menuItem -> { | |||
DataObject item = data.get(0); | |||
if (grid.isSelected(item)) { | |||
grid.deselect(item); | |||
if (grid.getSelectionModel().isSelected(item)) { | |||
grid.getSelectionModel().deselect(item); | |||
} else { | |||
grid.select(item); | |||
grid.getSelectionModel().select(item); | |||
} | |||
}); | |||
} | |||
@@ -432,10 +427,9 @@ public class GridBasics extends AbstractTestUIWithLog { | |||
private void createFooterMenu(MenuItem footerMenu) { | |||
footerMenu.addItem("Add default footer row", menuItem -> { | |||
FooterRow defaultFooter = grid.appendFooterRow(); | |||
grid.getColumns() | |||
.forEach(column -> defaultFooter.getCell(column) | |||
.setText(grid.getDefaultHeaderRow().getCell(column) | |||
.getText())); | |||
grid.getColumns().forEach( | |||
column -> defaultFooter.getCell(column).setText(grid | |||
.getDefaultHeaderRow().getCell(column).getText())); | |||
footerMenu.removeChild(menuItem); | |||
}); | |||
footerMenu.addItem("Append footer row", menuItem -> { |
@@ -16,7 +16,6 @@ | |||
package com.vaadin.tests.components.nativeselect; | |||
import com.vaadin.tests.components.AbstractListingFocusBlurTest; | |||
import com.vaadin.ui.AbstractSingleSelect; | |||
import com.vaadin.ui.NativeSelect; | |||
/** | |||
@@ -28,7 +27,7 @@ import com.vaadin.ui.NativeSelect; | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class NativeSelectFocusBlur extends | |||
AbstractListingFocusBlurTest<NativeSelect<Integer>, AbstractSingleSelect<Integer>.AbstractSingleSelection> { | |||
public class NativeSelectFocusBlur | |||
extends AbstractListingFocusBlurTest<NativeSelect<Integer>> { | |||
} |
@@ -15,15 +15,14 @@ | |||
*/ | |||
package com.vaadin.tests.components.radiobutton; | |||
import com.vaadin.data.SelectionModel; | |||
import java.util.LinkedHashMap; | |||
import java.util.stream.IntStream; | |||
import com.vaadin.server.FontAwesome; | |||
import com.vaadin.tests.components.abstractlisting.AbstractListingTestUI; | |||
import com.vaadin.ui.ItemCaptionGenerator; | |||
import com.vaadin.ui.RadioButtonGroup; | |||
import java.util.LinkedHashMap; | |||
import java.util.stream.IntStream; | |||
/** | |||
* Test UI for RadioButtonGroup component | |||
* | |||
@@ -50,9 +49,9 @@ public class RadioButtonGroupTestUI | |||
} | |||
protected void createSelectionMenu() { | |||
createClickAction( | |||
"Clear selection", selectionCategory, (component, item, | |||
data) -> component.getSelectionModel().deselectAll(), | |||
createClickAction("Clear selection", selectionCategory, | |||
(component, item, data) -> component.getSelectedItem() | |||
.ifPresent(component::deselect), | |||
""); | |||
Command<RadioButtonGroup<Object>, String> toggleSelection = (component, | |||
@@ -62,13 +61,14 @@ public class RadioButtonGroupTestUI | |||
.forEach(item -> createClickAction("Toggle " + item, | |||
selectionCategory, toggleSelection, item)); | |||
} | |||
private void createItemIconGeneratorMenu() { | |||
createBooleanAction("Use Item Icon Generator", "Item Icon Generator", false, | |||
this::useItemIconGenerator); | |||
createBooleanAction("Use Item Icon Generator", "Item Icon Generator", | |||
false, this::useItemIconGenerator); | |||
} | |||
private void useItemIconGenerator(RadioButtonGroup<Object> group, | |||
boolean activate, Object data) { | |||
boolean activate, Object data) { | |||
if (activate) { | |||
group.setItemIconGenerator( | |||
item -> FontAwesome.values()[getIndex(item) + 1]); | |||
@@ -85,20 +85,18 @@ public class RadioButtonGroupTestUI | |||
options.put("Custom Caption Generator", | |||
item -> item.toString() + " Caption"); | |||
createSelectAction("Item Caption Generator", "Item Caption Generator", options, | |||
"None", (radioButtonGroup, captionGenerator, data) -> { | |||
createSelectAction("Item Caption Generator", "Item Caption Generator", | |||
options, "None", (radioButtonGroup, captionGenerator, data) -> { | |||
radioButtonGroup.setItemCaptionGenerator(captionGenerator); | |||
radioButtonGroup.getDataSource().refreshAll(); | |||
}, true); | |||
} | |||
private void toggleSelection(String item) { | |||
SelectionModel.Single<Object> selectionModel = getComponent() | |||
.getSelectionModel(); | |||
if (selectionModel.isSelected(item)) { | |||
selectionModel.deselect(item); | |||
if (getComponent().isSelected(item)) { | |||
getComponent().deselect(item); | |||
} else { | |||
selectionModel.select(item); | |||
getComponent().select(item); | |||
} | |||
} | |||
@@ -108,7 +106,6 @@ public class RadioButtonGroupTestUI | |||
e -> log("Selected: " + e.getSelectedItem()))); | |||
} | |||
private int getIndex(Object item) { | |||
int index = item.toString().indexOf(' '); | |||
if (index < 0) { |
@@ -16,7 +16,6 @@ | |||
package com.vaadin.tests.components.radiobuttongroup; | |||
import com.vaadin.tests.components.AbstractListingFocusBlurTest; | |||
import com.vaadin.ui.AbstractSingleSelect; | |||
import com.vaadin.ui.RadioButtonGroup; | |||
/** | |||
@@ -28,7 +27,7 @@ import com.vaadin.ui.RadioButtonGroup; | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class RadioButtonGroupFocusBlur extends | |||
AbstractListingFocusBlurTest<RadioButtonGroup<Integer>, AbstractSingleSelect<Integer>.AbstractSingleSelection> { | |||
public class RadioButtonGroupFocusBlur | |||
extends AbstractListingFocusBlurTest<RadioButtonGroup<Integer>> { | |||
} |
@@ -4,19 +4,17 @@ import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Set; | |||
import java.util.Optional; | |||
import java.util.stream.Stream; | |||
import com.vaadin.annotations.Widgetset; | |||
import com.vaadin.data.SelectionModel; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.server.data.DataCommunicator; | |||
import com.vaadin.server.data.ListDataSource; | |||
import com.vaadin.server.data.Query; | |||
import com.vaadin.shared.data.DataCommunicatorConstants; | |||
import com.vaadin.tests.components.AbstractTestUIWithLog; | |||
import com.vaadin.tests.widgetset.TestingWidgetSet; | |||
import com.vaadin.ui.AbstractListing; | |||
import com.vaadin.ui.AbstractSingleSelect; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.HorizontalLayout; | |||
@@ -44,26 +42,32 @@ public class DummyData extends AbstractTestUIWithLog { | |||
* Simplified server only selection model. Selection state passed in data, | |||
* shown as bold text. | |||
*/ | |||
private static class DummySelectionModel implements SelectionModel<String> { | |||
public static class DummyComponent extends AbstractSingleSelect<String> { | |||
private String selected; | |||
private DataCommunicator<String> communicator; | |||
private DummyComponent() { | |||
addDataGenerator((str, json) -> { | |||
json.put(DataCommunicatorConstants.DATA, str); | |||
if (isSelected(str)) { | |||
json.put(DataCommunicatorConstants.SELECTED, true); | |||
} | |||
}); | |||
} | |||
@Override | |||
public Set<String> getSelectedItems() { | |||
if (selected != null) { | |||
return Collections.singleton(selected); | |||
} | |||
return Collections.emptySet(); | |||
public Optional<String> getSelectedItem() { | |||
return Optional.ofNullable(selected); | |||
} | |||
@Override | |||
public void select(String item) { | |||
if (selected != null) { | |||
communicator.refresh(selected); | |||
getDataCommunicator().refresh(selected); | |||
} | |||
selected = item; | |||
if (selected != null) { | |||
communicator.refresh(selected); | |||
getDataCommunicator().refresh(selected); | |||
} | |||
} | |||
@@ -73,25 +77,6 @@ public class DummyData extends AbstractTestUIWithLog { | |||
select(null); | |||
} | |||
} | |||
private void setCommunicator(DataCommunicator<String> dataComm) { | |||
communicator = dataComm; | |||
} | |||
} | |||
public static class DummyComponent | |||
extends AbstractListing<String, DummySelectionModel> { | |||
private DummyComponent() { | |||
setSelectionModel(new DummySelectionModel()); | |||
addDataGenerator((str, json) -> { | |||
json.put(DataCommunicatorConstants.DATA, str); | |||
if (isSelected(str)) { | |||
json.put(DataCommunicatorConstants.SELECTED, true); | |||
} | |||
}); | |||
getSelectionModel().setCommunicator(getDataCommunicator()); | |||
} | |||
} | |||
@Override |