aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/main/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/main/java/com')
-rw-r--r--server/src/main/java/com/vaadin/data/Binder.java93
-rw-r--r--server/src/main/java/com/vaadin/event/selection/MultiSelectionEvent.java19
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java66
-rw-r--r--server/src/main/java/com/vaadin/ui/CheckBoxGroup.java62
4 files changed, 195 insertions, 45 deletions
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java
index 88549e12c3..d2114e8120 100644
--- a/server/src/main/java/com/vaadin/data/Binder.java
+++ b/server/src/main/java/com/vaadin/data/Binder.java
@@ -37,7 +37,9 @@ import com.vaadin.data.util.converter.StringToIntegerConverter;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.UserError;
import com.vaadin.shared.Registration;
+import com.vaadin.shared.data.selection.SelectionModel.Multi;
import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.AbstractMultiSelect;
import com.vaadin.ui.AbstractSingleSelect;
import com.vaadin.ui.Component;
import com.vaadin.ui.Label;
@@ -748,6 +750,45 @@ public class Binder<BEAN> implements Serializable {
}
/**
+ * Creates a new binding for the given multi select component. The returned
+ * binding may be further configured before invoking
+ * {@link Binding#bind(Function, BiConsumer) Binding.bind} which completes
+ * the binding. Until {@code Binding.bind} is called, the binding has no
+ * effect.
+ *
+ * @param <SELECTVALUE>
+ * the item type of the select
+ * @param select
+ * the select to be bound, not null
+ * @return the new binding
+ */
+ public <SELECTVALUE> Binding<BEAN, Set<SELECTVALUE>, Set<SELECTVALUE>> forSelect(
+ AbstractMultiSelect<SELECTVALUE> select) {
+ return forField(new HasValue<Set<SELECTVALUE>>() {
+
+ @Override
+ public void setValue(Set<SELECTVALUE> value) {
+ Multi<SELECTVALUE> selectionModel = select.getSelectionModel();
+ selectionModel.deselectAll();
+ value.forEach(selectionModel::select);
+ }
+
+ @Override
+ public Set<SELECTVALUE> getValue() {
+ return select.getSelectionModel().getSelectedItems();
+ }
+
+ @Override
+ public Registration addValueChangeListener(
+ ValueChangeListener<? super Set<SELECTVALUE>> listener) {
+ return select.addSelectionListener(
+ e -> listener.accept(new ValueChange<>(select,
+ getValue(), e.isUserOriginated())));
+ }
+ });
+ }
+
+ /**
* Binds a field to a bean property represented by the given getter and
* setter pair. The functions are used to update the field value from the
* property and to store the field value to the property, respectively.
@@ -850,6 +891,58 @@ public class Binder<BEAN> implements Serializable {
}
/**
+ * Binds a multi select to a bean property represented by the given getter
+ * and setter pair. The functions are used to update the set of selected
+ * items from the property and to store the selection to the property,
+ * respectively.
+ * <p>
+ * Use the {@link #forSelect(AbstractMultiSelect)} method instead if you
+ * want to further configure the new binding.
+ * <p>
+ * When a bean is bound with {@link Binder#bind(BEAN)}, the set of selected
+ * items are set to the return value of the given getter. The property value
+ * is then updated via the given setter whenever the selected items changes.
+ * The setter may be null; in that case the property value is never updated
+ * and the binding is said to be <i>read-only</i>.
+ * <p>
+ * If the Binder is already bound to some item, the newly bound select is
+ * associated with the corresponding bean property as described above.
+ * <p>
+ * The getter and setter can be arbitrary functions, for instance
+ * implementing user-defined conversion or validation. However, in the most
+ * basic use case you can simply pass a pair of method references to this
+ * method as follows:
+ *
+ * <pre>
+ * class Feature {
+ * public enum Browser { CHROME, EDGE, FIREFOX, IE, OPERA, SAFARI }
+
+ * public Set&lt;Browser> getSupportedBrowsers() { ... }
+ * public void setSupportedBrowsers(Set&lt;Browser> title) { ... }
+ * }
+ *
+ * CheckBoxGroup<Title> browserSelect = new CheckBoxGroup<>();
+ * browserSelect.setItems(Browser.values());
+ * binder.bind(browserSelect, Feature::getSupportedBrowsers, Feature::setSupportedBrowsers);
+ * </pre>
+ *
+ * @param <SELECTVALUE>
+ * the item type of the select
+ * @param select
+ * the select to bind, not null
+ * @param getter
+ * the function to get the set of selected items, not null
+ * @param setter
+ * the function to save the set of selected items or null if
+ * read-only
+ */
+ public <SELECTVALUE> void bind(AbstractMultiSelect<SELECTVALUE> select,
+ Function<BEAN, Set<SELECTVALUE>> getter,
+ BiConsumer<BEAN, Set<SELECTVALUE>> setter) {
+ forSelect(select).bind(getter, setter);
+ }
+
+ /**
* Binds the given bean to all the fields added to this Binder. To remove
* the binding, call {@link #unbind()}.
* <p>
diff --git a/server/src/main/java/com/vaadin/event/selection/MultiSelectionEvent.java b/server/src/main/java/com/vaadin/event/selection/MultiSelectionEvent.java
index 81c8478721..1de911f353 100644
--- a/server/src/main/java/com/vaadin/event/selection/MultiSelectionEvent.java
+++ b/server/src/main/java/com/vaadin/event/selection/MultiSelectionEvent.java
@@ -18,7 +18,7 @@ package com.vaadin.event.selection;
import java.util.Collections;
import java.util.Set;
-import com.vaadin.event.ConnectorEvent;
+import com.vaadin.data.HasValue.ValueChange;
import com.vaadin.shared.data.selection.SelectionModel;
import com.vaadin.ui.AbstractListing;
@@ -33,10 +33,9 @@ import com.vaadin.ui.AbstractListing;
* @param <T>
* the data type of the selection model
*/
-public class MultiSelectionEvent<T> extends ConnectorEvent {
+public class MultiSelectionEvent<T> extends ValueChange<Set<T>> {
- private Set<T> oldSelection;
- private Set<T> newSelection;
+ private final Set<T> oldSelection;
/**
* Creates a new event.
@@ -47,13 +46,16 @@ public class MultiSelectionEvent<T> extends ConnectorEvent {
* the old set of selected items
* @param newSelection
* the new set of selected items
+ * @param userOriginated
+ * {@code true} if this event originates from the client,
+ * {@code false} otherwise.
*/
public MultiSelectionEvent(
AbstractListing<T, SelectionModel.Multi<T>> source,
- Set<T> oldSelection, Set<T> newSelection) {
- super(source);
+ Set<T> oldSelection, Set<T> newSelection, boolean userOriginated) {
+ super(source, Collections.unmodifiableSet(newSelection),
+ userOriginated);
this.oldSelection = oldSelection;
- this.newSelection = newSelection;
}
/**
@@ -62,7 +64,7 @@ public class MultiSelectionEvent<T> extends ConnectorEvent {
* @return a set of items selected after the selection was changed
*/
public Set<T> getNewSelection() {
- return Collections.unmodifiableSet(newSelection);
+ return getValue();
}
/**
@@ -73,5 +75,4 @@ public class MultiSelectionEvent<T> extends ConnectorEvent {
public Set<T> getOldSelection() {
return Collections.unmodifiableSet(oldSelection);
}
-
}
diff --git a/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java b/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
new file mode 100644
index 0000000000..1d17841812
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
@@ -0,0 +1,66 @@
+/*
+ * 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.lang.reflect.Method;
+import java.util.Objects;
+
+import com.vaadin.event.selection.MultiSelectionEvent;
+import com.vaadin.event.selection.MultiSelectionListener;
+import com.vaadin.shared.Registration;
+import com.vaadin.shared.data.selection.SelectionModel.Multi;
+import com.vaadin.util.ReflectTools;
+
+/**
+ * Base class for listing components that allow selecting multiple items.
+ *
+ * @param <T>
+ * item type
+ * @author Vaadin Ltd
+ * @since 8.0
+ */
+public abstract class AbstractMultiSelect<T>
+ extends AbstractListing<T, Multi<T>> {
+
+ @Deprecated
+ private static final Method SELECTION_CHANGE_METHOD = ReflectTools
+ .findMethod(MultiSelectionListener.class, "accept",
+ MultiSelectionEvent.class);
+
+ /**
+ * Creates a new multi select with an empty data source.
+ */
+ protected AbstractMultiSelect() {
+ super();
+ }
+
+ /**
+ * 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</code>
+ * @return a registration for the listener
+ */
+ public Registration addSelectionListener(
+ MultiSelectionListener<T> listener) {
+ Objects.requireNonNull(listener, "listener cannot be null");
+ addListener(MultiSelectionEvent.class, listener,
+ SELECTION_CHANGE_METHOD);
+ return () -> removeListener(MultiSelectionEvent.class, listener);
+ }
+
+} \ No newline at end of file
diff --git a/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java b/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java
index 8687aa2b92..8525dc6cbb 100644
--- a/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java
+++ b/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java
@@ -16,7 +16,6 @@
package com.vaadin.ui;
-import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
@@ -29,17 +28,14 @@ import java.util.function.Predicate;
import com.vaadin.data.Listing;
import com.vaadin.event.selection.MultiSelectionEvent;
-import com.vaadin.event.selection.MultiSelectionListener;
import com.vaadin.server.Resource;
import com.vaadin.server.ResourceReference;
import com.vaadin.server.data.DataGenerator;
import com.vaadin.server.data.DataSource;
-import com.vaadin.shared.Registration;
import com.vaadin.shared.data.selection.SelectionModel;
import com.vaadin.shared.data.selection.SelectionServerRpc;
import com.vaadin.shared.ui.optiongroup.CheckBoxGroupConstants;
import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState;
-import com.vaadin.util.ReflectTools;
import elemental.json.JsonObject;
@@ -52,8 +48,7 @@ import elemental.json.JsonObject;
* @author Vaadin Ltd
* @since 8.0
*/
-public class CheckBoxGroup<T>
- extends AbstractListing<T, SelectionModel.Multi<T>> {
+public class CheckBoxGroup<T> extends AbstractMultiSelect<T> {
private final class SimpleMultiSelectModel
implements SelectionModel.Multi<T> {
@@ -62,11 +57,16 @@ public class CheckBoxGroup<T>
@Override
public void select(T item) {
+ // Not user originated
+ select(item, false);
+ }
+
+ private void select(T item, boolean userOriginated) {
if (selection.contains(item)) {
return;
}
- updateSelection(set -> set.add(item));
+ updateSelection(set -> set.add(item), userOriginated);
}
@Override
@@ -76,11 +76,16 @@ public class CheckBoxGroup<T>
@Override
public void deselect(T item) {
+ // Not user originated
+ deselect(item, false);
+ }
+
+ private void deselect(T item, boolean userOriginated) {
if (!selection.contains(item)) {
return;
}
- updateSelection(set -> set.remove(item));
+ updateSelection(set -> set.remove(item), userOriginated);
}
@Override
@@ -89,16 +94,17 @@ public class CheckBoxGroup<T>
return;
}
- updateSelection(Set::clear);
+ updateSelection(Set::clear, false);
}
- private void updateSelection(Consumer<Set<T>> handler) {
+ 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<>(CheckBoxGroup.this,
- oldSelection, newSelection));
+ oldSelection, newSelection, userOriginated));
getDataCommunicator().reset();
}
@@ -109,11 +115,6 @@ public class CheckBoxGroup<T>
}
}
- @Deprecated
- private static final Method SELECTION_CHANGE_METHOD = ReflectTools
- .findMethod(MultiSelectionListener.class, "accept",
- MultiSelectionEvent.class);
-
private Function<T, Resource> itemIconProvider = item -> null;
private Function<T, String> itemCaptionProvider = String::valueOf;
@@ -172,14 +173,14 @@ public class CheckBoxGroup<T>
@Override
public void select(String key) {
- getItemForSelectionChange(key)
- .ifPresent(getSelectionModel()::select);
+ getItemForSelectionChange(key).ifPresent(
+ item -> getSelectionModel().select(item, true));
}
@Override
public void deselect(String key) {
- getItemForSelectionChange(key)
- .ifPresent(getSelectionModel()::deselect);
+ getItemForSelectionChange(key).ifPresent(
+ item -> getSelectionModel().deselect(item, true));
}
private Optional<T> getItemForSelectionChange(String key) {
@@ -190,6 +191,11 @@ public class CheckBoxGroup<T>
return Optional.of(item);
}
+
+ private SimpleMultiSelectModel getSelectionModel() {
+ return (SimpleMultiSelectModel) CheckBoxGroup.this
+ .getSelectionModel();
+ }
});
addDataGenerator(new DataGenerator<T>() {
@@ -330,20 +336,4 @@ public class CheckBoxGroup<T>
Objects.nonNull(itemEnabledProvider);
this.itemEnabledProvider = itemEnabledProvider;
}
-
- /**
- * 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</code>
- * @return a registration for the listener
- */
- public Registration addSelectionListener(
- MultiSelectionListener<T> listener) {
- Objects.requireNonNull(listener, "listener cannot be null");
- addListener(MultiSelectionEvent.class, listener,
- SELECTION_CHANGE_METHOD);
- return () -> removeListener(MultiSelectionEvent.class, listener);
- }
}