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.

RadioButtonGroup.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. * Copyright 2000-2016 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.ui;
  17. import java.util.Collection;
  18. import java.util.Objects;
  19. import java.util.Set;
  20. import org.jsoup.nodes.Element;
  21. import com.vaadin.data.HasDataProvider;
  22. import com.vaadin.data.provider.DataGenerator;
  23. import com.vaadin.data.provider.DataProvider;
  24. import com.vaadin.event.FieldEvents.BlurEvent;
  25. import com.vaadin.event.FieldEvents.BlurListener;
  26. import com.vaadin.event.FieldEvents.BlurNotifier;
  27. import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
  28. import com.vaadin.event.FieldEvents.FocusEvent;
  29. import com.vaadin.event.FieldEvents.FocusListener;
  30. import com.vaadin.event.FieldEvents.FocusNotifier;
  31. import com.vaadin.server.Resource;
  32. import com.vaadin.server.ResourceReference;
  33. import com.vaadin.server.SerializablePredicate;
  34. import com.vaadin.shared.Registration;
  35. import com.vaadin.shared.ui.ListingJsonConstants;
  36. import com.vaadin.shared.ui.optiongroup.RadioButtonGroupState;
  37. import com.vaadin.ui.components.grid.DescriptionGenerator;
  38. import com.vaadin.ui.declarative.DesignContext;
  39. import com.vaadin.ui.declarative.DesignFormatter;
  40. import elemental.json.JsonObject;
  41. /**
  42. * A group of RadioButtons. Individual radiobuttons are made from items supplied
  43. * by a {@link DataProvider}. RadioButtons may have captions and icons.
  44. *
  45. * @param <T>
  46. * item type
  47. * @author Vaadin Ltd
  48. * @since 8.0
  49. */
  50. public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
  51. implements FocusNotifier, BlurNotifier, HasDataProvider<T> {
  52. private SerializablePredicate<T> itemEnabledProvider = item -> true;
  53. private DescriptionGenerator<T> descriptionGenerator = item -> null;
  54. /**
  55. * Constructs a new RadioButtonGroup with caption.
  56. *
  57. * @param caption
  58. * caption text
  59. */
  60. public RadioButtonGroup(String caption) {
  61. this();
  62. setCaption(caption);
  63. }
  64. /**
  65. * Constructs a new RadioButtonGroup with caption and DataProvider.
  66. *
  67. * @param caption
  68. * the caption text
  69. * @param dataProvider
  70. * the data provider, not null
  71. * @see HasDataProvider#setDataProvider(DataProvider)
  72. */
  73. public RadioButtonGroup(String caption, DataProvider<T, ?> dataProvider) {
  74. this(caption);
  75. setDataProvider(dataProvider);
  76. }
  77. /**
  78. * Constructs a new RadioButtonGroup with caption and DataProvider
  79. * containing given items.
  80. *
  81. * @param caption
  82. * the caption text
  83. * @param items
  84. * the data items to use, not null
  85. * @see #setItems(Collection)
  86. */
  87. public RadioButtonGroup(String caption, Collection<T> items) {
  88. this(caption, DataProvider.ofCollection(items));
  89. }
  90. /**
  91. * Constructs a new RadioButtonGroup.
  92. */
  93. public RadioButtonGroup() {
  94. registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
  95. addDataGenerator(new DataGenerator<T>() {
  96. @Override
  97. public void generateData(T data, JsonObject jsonObject) {
  98. String caption = getItemCaptionGenerator().apply(data);
  99. if (caption != null) {
  100. jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_VALUE,
  101. caption);
  102. } else {
  103. jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_VALUE, "");
  104. }
  105. String description = getItemDescriptionGenerator().apply(data);
  106. if (description != null) {
  107. jsonObject.put(
  108. ListingJsonConstants.JSONKEY_ITEM_DESCRIPTION,
  109. description);
  110. }
  111. Resource icon = getItemIconGenerator().apply(data);
  112. if (icon != null) {
  113. String iconUrl = ResourceReference
  114. .create(icon, RadioButtonGroup.this, null).getURL();
  115. jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_ICON,
  116. iconUrl);
  117. }
  118. if (!itemEnabledProvider.test(data)) {
  119. jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_DISABLED,
  120. true);
  121. }
  122. if (isSelected(data)) {
  123. jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED,
  124. true);
  125. }
  126. }
  127. @Override
  128. public void destroyData(T data) {
  129. }
  130. });
  131. }
  132. /**
  133. * Sets whether html is allowed in the item captions. If set to true, the
  134. * captions are passed to the browser as html and the developer is
  135. * responsible for ensuring no harmful html is used. If set to false, the
  136. * content is passed to the browser as plain text.
  137. *
  138. * @param htmlContentAllowed
  139. * true if the captions are used as html, false if used as plain
  140. * text
  141. */
  142. public void setHtmlContentAllowed(boolean htmlContentAllowed) {
  143. getState().htmlContentAllowed = htmlContentAllowed;
  144. }
  145. /**
  146. * Checks whether captions are interpreted as html or plain text.
  147. *
  148. * @return true if the captions are used as html, false if used as plain
  149. * text
  150. * @see #setHtmlContentAllowed(boolean)
  151. */
  152. public boolean isHtmlContentAllowed() {
  153. return getState(false).htmlContentAllowed;
  154. }
  155. @Override
  156. protected RadioButtonGroupState getState() {
  157. return (RadioButtonGroupState) super.getState();
  158. }
  159. @Override
  160. protected RadioButtonGroupState getState(boolean markAsDirty) {
  161. return (RadioButtonGroupState) super.getState(markAsDirty);
  162. }
  163. @Override
  164. public IconGenerator<T> getItemIconGenerator() {
  165. return super.getItemIconGenerator();
  166. }
  167. @Override
  168. public void setItemIconGenerator(IconGenerator<T> itemIconGenerator) {
  169. super.setItemIconGenerator(itemIconGenerator);
  170. }
  171. @Override
  172. public ItemCaptionGenerator<T> getItemCaptionGenerator() {
  173. return super.getItemCaptionGenerator();
  174. }
  175. @Override
  176. public void setItemCaptionGenerator(
  177. ItemCaptionGenerator<T> itemCaptionGenerator) {
  178. super.setItemCaptionGenerator(itemCaptionGenerator);
  179. }
  180. /**
  181. * Returns the item enabled predicate.
  182. *
  183. * @return the item enabled predicate
  184. * @see #setItemEnabledProvider
  185. */
  186. public SerializablePredicate<T> getItemEnabledProvider() {
  187. return itemEnabledProvider;
  188. }
  189. /**
  190. * Sets the item enabled predicate for this radiobutton group. The predicate
  191. * is applied to each item to determine whether the item should be enabled
  192. * (true) or disabled (false). Disabled items are displayed as grayed out
  193. * and the user cannot select them. The default predicate always returns
  194. * true (all the items are enabled).
  195. *
  196. * @param itemEnabledProvider
  197. * the item enable predicate, not null
  198. */
  199. public void setItemEnabledProvider(
  200. SerializablePredicate<T> itemEnabledProvider) {
  201. Objects.requireNonNull(itemEnabledProvider);
  202. this.itemEnabledProvider = itemEnabledProvider;
  203. }
  204. /**
  205. * Sets the description generator that is used for generating descriptions
  206. * for items. Description is shown as a tooltip when hovering on
  207. * corresponding element. If the generator returns {@code null}, no tooltip
  208. * is shown.
  209. *
  210. * @param descriptionGenerator
  211. * the item description generator to set, not {@code null}
  212. *
  213. * @since 8.2
  214. */
  215. public void setItemDescriptionGenerator(
  216. DescriptionGenerator<T> descriptionGenerator) {
  217. Objects.requireNonNull(descriptionGenerator);
  218. if (this.descriptionGenerator != descriptionGenerator) {
  219. this.descriptionGenerator = descriptionGenerator;
  220. getDataProvider().refreshAll();
  221. }
  222. }
  223. /**
  224. * Gets the item description generator.
  225. *
  226. * @return the item description generator
  227. *
  228. * @since 8.2
  229. */
  230. public DescriptionGenerator<T> getItemDescriptionGenerator() {
  231. return descriptionGenerator;
  232. }
  233. @Override
  234. public Registration addFocusListener(FocusListener listener) {
  235. return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
  236. FocusListener.focusMethod);
  237. }
  238. @Override
  239. public Registration addBlurListener(BlurListener listener) {
  240. return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
  241. BlurListener.blurMethod);
  242. }
  243. @Override
  244. protected void readItems(Element design, DesignContext context) {
  245. setItemEnabledProvider(new DeclarativeItemEnabledProvider<>());
  246. super.readItems(design, context);
  247. }
  248. @SuppressWarnings({ "unchecked", "rawtypes" })
  249. @Override
  250. protected T readItem(Element child, Set<T> selected,
  251. DesignContext context) {
  252. T item = super.readItem(child, selected, context);
  253. SerializablePredicate<T> provider = getItemEnabledProvider();
  254. if (provider instanceof DeclarativeItemEnabledProvider) {
  255. if (child.hasAttr("disabled")) {
  256. ((DeclarativeItemEnabledProvider) provider).addDisabled(item);
  257. }
  258. } else {
  259. throw new IllegalStateException(String.format(
  260. "Don't know how "
  261. + "to disable item using current item enabled provider '%s'",
  262. provider.getClass().getName()));
  263. }
  264. return item;
  265. }
  266. @Override
  267. protected Element writeItem(Element design, T item, DesignContext context) {
  268. Element elem = super.writeItem(design, item, context);
  269. if (!getItemEnabledProvider().test(item)) {
  270. elem.attr("disabled", "");
  271. }
  272. if (isHtmlContentAllowed()) {
  273. // need to unencode HTML entities. AbstractMultiSelect.writeDesign
  274. // can't check if HTML content is allowed, so it always encodes
  275. // entities like '>', '<' and '&'; in case HTML content is allowed
  276. // this is undesirable so we need to unencode entities. Entities
  277. // other than '<' and '>' will be taken care by Jsoup.
  278. elem.html(DesignFormatter.decodeFromTextNode(elem.html()));
  279. }
  280. return elem;
  281. }
  282. @Override
  283. public DataProvider<T, ?> getDataProvider() {
  284. return internalGetDataProvider();
  285. }
  286. @Override
  287. public void setDataProvider(DataProvider<T, ?> dataProvider) {
  288. internalSetDataProvider(dataProvider);
  289. }
  290. }