You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VListSelect.java 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.ui;
  17. import java.util.ArrayList;
  18. import java.util.HashSet;
  19. import java.util.List;
  20. import java.util.Objects;
  21. import java.util.Set;
  22. import java.util.function.BiConsumer;
  23. import com.google.gwt.user.client.ui.Composite;
  24. import com.google.gwt.user.client.ui.FlowPanel;
  25. import com.google.gwt.user.client.ui.HasEnabled;
  26. import com.google.gwt.user.client.ui.ListBox;
  27. import com.vaadin.client.BrowserInfo;
  28. import com.vaadin.client.FastStringSet;
  29. import com.vaadin.client.Focusable;
  30. import com.vaadin.client.connectors.AbstractMultiSelectConnector.MultiSelectWidget;
  31. import com.vaadin.shared.Registration;
  32. import com.vaadin.shared.ui.listselect.ListSelectState;
  33. import elemental.json.JsonObject;
  34. /**
  35. * A simple list select for selecting multiple items.
  36. *
  37. * @author Vaadin Ltd
  38. */
  39. public class VListSelect extends Composite
  40. implements Field, Focusable, HasEnabled, MultiSelectWidget {
  41. private List<BiConsumer<Set<String>, Set<String>>> selectionChangeListeners = new ArrayList<>();
  42. /** Container for select. Kept for DOM backwards compatibility. */
  43. protected final FlowPanel container;
  44. /** The select component. */
  45. protected final ListBox select;
  46. private boolean enabled;
  47. private boolean readOnly;
  48. private FastStringSet selectedItemKeys = FastStringSet.create();
  49. /**
  50. * Constructs a simple ListSelect widget in multiselect mode.
  51. */
  52. public VListSelect() {
  53. container = new FlowPanel();
  54. initWidget(container);
  55. select = new ListBox();
  56. select.setMultipleSelect(true);
  57. // Add event handlers
  58. select.addClickHandler(
  59. clickEvent -> selectionEvent(clickEvent.getSource()));
  60. select.addChangeHandler(
  61. changeEvent -> selectionEvent(changeEvent.getSource()));
  62. container.add(select);
  63. updateEnabledState();
  64. setStylePrimaryName(ListSelectState.PRIMARY_STYLENAME);
  65. }
  66. @Override
  67. public void setStylePrimaryName(String style) {
  68. super.setStylePrimaryName(style);
  69. select.setStyleName(style + "-select");
  70. }
  71. /**
  72. * Sets the number of visible items for the list select.
  73. *
  74. * @param rows
  75. * the number of items to show
  76. * @see ListBox#setVisibleItemCount(int)
  77. */
  78. public void setRows(int rows) {
  79. if (select.getVisibleItemCount() != rows) {
  80. select.setVisibleItemCount(rows);
  81. }
  82. }
  83. /**
  84. * Returns the number of visible items for the list select.
  85. *
  86. * @return the number of items to show
  87. * @see ListBox#setVisibleItemCount(int)
  88. */
  89. public int getRows() {
  90. return select.getVisibleItemCount();
  91. }
  92. @Override
  93. public Registration addSelectionChangeListener(
  94. BiConsumer<Set<String>, Set<String>> listener) {
  95. Objects.nonNull(listener);
  96. selectionChangeListeners.add(listener);
  97. return (Registration) () -> selectionChangeListeners.remove(listener);
  98. }
  99. @Override
  100. public void setItems(List<JsonObject> items) {
  101. selectedItemKeys = FastStringSet.create();
  102. for (int i = 0; i < items.size(); i++) {
  103. final JsonObject item = items.get(i);
  104. // reuse existing option if possible
  105. String key = MultiSelectWidget.getKey(item);
  106. if (BrowserInfo.get().isIE11() && key != null) {
  107. // IE11 doesn't handle numerical keys well on Win7,
  108. // prevent incorrect type handling with extra character
  109. key += " ";
  110. }
  111. if (i < select.getItemCount()) {
  112. select.setItemText(i, MultiSelectWidget.getCaption(item));
  113. select.setValue(i, key);
  114. } else {
  115. select.addItem(MultiSelectWidget.getCaption(item), key);
  116. }
  117. final boolean selected = MultiSelectWidget.isSelected(item);
  118. select.setItemSelected(i, selected);
  119. if (selected) {
  120. selectedItemKeys.add(key);
  121. }
  122. }
  123. // remove extra
  124. for (int i = select.getItemCount() - 1; i >= items.size(); i--) {
  125. select.removeItem(i);
  126. }
  127. }
  128. /**
  129. * Gets the currently selected item values.
  130. *
  131. * @return the currently selected item keys
  132. */
  133. protected FastStringSet getSelectedItems() {
  134. final FastStringSet selectedItemKeys = FastStringSet.create();
  135. for (int i = 0; i < select.getItemCount(); i++) {
  136. if (select.isItemSelected(i)) {
  137. String key = select.getValue(i);
  138. if (BrowserInfo.get().isIE11() && key != null) {
  139. // remove the IE11 workaround
  140. key = key.trim();
  141. }
  142. selectedItemKeys.add(key);
  143. }
  144. }
  145. return selectedItemKeys;
  146. }
  147. private void selectionEvent(Object source) {
  148. if (source == select) {
  149. // selection can change by adding and at the same time removing
  150. // previous keys, or by just adding (e.g. when modifier keys are
  151. // pressed)
  152. final Set<String> newSelectedItemKeys = new HashSet<>();
  153. final Set<String> removedItemKeys = new HashSet<>();
  154. for (int i = 0; i < select.getItemCount(); i++) {
  155. String key = select.getValue(i);
  156. boolean selected = select.isItemSelected(i);
  157. boolean wasSelected = selectedItemKeys.contains(key);
  158. if (selected && !wasSelected) {
  159. newSelectedItemKeys.add(key);
  160. selectedItemKeys.add(key);
  161. } else if (!selected && wasSelected) {
  162. removedItemKeys.add(key);
  163. selectedItemKeys.remove(key);
  164. }
  165. }
  166. selectionChangeListeners.forEach(
  167. l -> l.accept(newSelectedItemKeys, removedItemKeys));
  168. }
  169. }
  170. @Override
  171. public void setHeight(String height) {
  172. select.setHeight(height);
  173. super.setHeight(height);
  174. }
  175. @Override
  176. public void setWidth(String width) {
  177. select.setWidth(width);
  178. super.setWidth(width);
  179. }
  180. /**
  181. * Sets the tab index.
  182. *
  183. * @param tabIndex
  184. * the tab index to set
  185. */
  186. public void setTabIndex(int tabIndex) {
  187. select.setTabIndex(tabIndex);
  188. }
  189. /**
  190. * Gets the tab index.
  191. *
  192. * @return the tab index
  193. */
  194. public int getTabIndex() {
  195. return select.getTabIndex();
  196. }
  197. /**
  198. * Sets this select as read only, meaning selection cannot be changed.
  199. *
  200. * @param readOnly
  201. * {@code true} for read only, {@code false} for not read only
  202. */
  203. public void setReadOnly(boolean readOnly) {
  204. if (this.readOnly != readOnly) {
  205. this.readOnly = readOnly;
  206. updateEnabledState();
  207. }
  208. }
  209. /**
  210. * Returns {@code true} if this select is in read only mode, {@code false}
  211. * if not.
  212. *
  213. * @return {@code true} for read only, {@code false} for not read only
  214. */
  215. public boolean isReadOnly() {
  216. return readOnly;
  217. }
  218. @Override
  219. public void setEnabled(boolean enabled) {
  220. if (this.enabled != enabled) {
  221. this.enabled = enabled;
  222. updateEnabledState();
  223. }
  224. }
  225. @Override
  226. public boolean isEnabled() {
  227. return enabled;
  228. }
  229. private void updateEnabledState() {
  230. select.setEnabled(isEnabled() && !isReadOnly());
  231. }
  232. @Override
  233. public void focus() {
  234. select.setFocus(true);
  235. }
  236. }