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

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