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.

CheckBox.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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.ui;
  17. import java.util.ArrayList;
  18. import java.util.Collection;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import java.util.Objects;
  22. import java.util.StringTokenizer;
  23. import org.jsoup.nodes.Attributes;
  24. import org.jsoup.nodes.Element;
  25. import com.vaadin.event.FieldEvents;
  26. import com.vaadin.event.FieldEvents.BlurEvent;
  27. import com.vaadin.event.FieldEvents.BlurListener;
  28. import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
  29. import com.vaadin.event.FieldEvents.FocusEvent;
  30. import com.vaadin.event.FieldEvents.FocusListener;
  31. import com.vaadin.shared.MouseEventDetails;
  32. import com.vaadin.shared.Registration;
  33. import com.vaadin.shared.ui.ComponentStateUtil;
  34. import com.vaadin.shared.ui.checkbox.CheckBoxServerRpc;
  35. import com.vaadin.shared.ui.checkbox.CheckBoxState;
  36. import com.vaadin.ui.declarative.DesignAttributeHandler;
  37. import com.vaadin.ui.declarative.DesignContext;
  38. public class CheckBox extends AbstractField<Boolean>
  39. implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier {
  40. private CheckBoxServerRpc rpc = (boolean checked,
  41. MouseEventDetails mouseEventDetails) -> {
  42. if (isReadOnly()) {
  43. return;
  44. }
  45. /*
  46. * Client side updates the state before sending the event so we need to
  47. * make sure the cached state is updated to match the client. If we do
  48. * not do this, a reverting setValue() call in a listener will not cause
  49. * the new state to be sent to the client.
  50. *
  51. * See #11028, #10030.
  52. */
  53. getUI().getConnectorTracker().getDiffState(CheckBox.this).put("checked",
  54. checked);
  55. final Boolean oldValue = getValue();
  56. final Boolean newValue = checked;
  57. if (!newValue.equals(oldValue)) {
  58. // The event is only sent if the switch state is changed
  59. setValue(newValue, true);
  60. }
  61. };
  62. private CheckBoxInputElement checkBoxInputElement = null;
  63. private CheckBoxLabelElement checkBoxLabelElement = null;
  64. /**
  65. * The inner input element of the CheckBox.
  66. */
  67. public static class CheckBoxInputElement implements HasStyleNames {
  68. private final CheckBox checkBox;
  69. private CheckBoxInputElement(CheckBox checkBox) {
  70. this.checkBox = checkBox;
  71. }
  72. @Override
  73. // Implementation copied from AbstractComponent
  74. public String getStyleName() {
  75. // replaced String with StringBuilder
  76. StringBuilder s = new StringBuilder();
  77. if (ComponentStateUtil
  78. .hasStyles(checkBox.getState(false).inputStyles)) {
  79. for (final Iterator<String> it = checkBox
  80. .getState(false).inputStyles.iterator(); it
  81. .hasNext();) {
  82. s.append(it.next());
  83. if (it.hasNext()) {
  84. s.append(" ");
  85. }
  86. }
  87. }
  88. return s.toString();
  89. }
  90. @Override
  91. // Implementation copied from AbstractComponent
  92. public void setStyleName(String style) {
  93. if (style == null || style.isEmpty()) {
  94. checkBox.getState().inputStyles = null;
  95. return;
  96. }
  97. if (checkBox.getState().inputStyles == null) {
  98. checkBox.getState().inputStyles = new ArrayList<>();
  99. }
  100. List<String> styles = checkBox.getState().inputStyles;
  101. styles.clear();
  102. StringTokenizer tokenizer = new StringTokenizer(style, " ");
  103. while (tokenizer.hasMoreTokens()) {
  104. styles.add(tokenizer.nextToken());
  105. }
  106. }
  107. @Override
  108. // Implementation copied from AbstractComponent
  109. public void addStyleName(String style) {
  110. if (style == null || style.isEmpty()) {
  111. return;
  112. }
  113. if (checkBox.getState().inputStyles != null
  114. && checkBox.getState().inputStyles.contains(style)) {
  115. return;
  116. }
  117. if (style.contains(" ")) {
  118. // Split space separated style names and add them one by one.
  119. StringTokenizer tokenizer = new StringTokenizer(style, " ");
  120. while (tokenizer.hasMoreTokens()) {
  121. addStyleName(tokenizer.nextToken());
  122. }
  123. return;
  124. }
  125. if (checkBox.getState().inputStyles == null) {
  126. checkBox.getState().inputStyles = new ArrayList<>();
  127. }
  128. List<String> styles = checkBox.getState().inputStyles;
  129. styles.add(style);
  130. }
  131. @Override
  132. // Implementation copied from AbstractComponent
  133. public void removeStyleName(String style) {
  134. if (ComponentStateUtil.hasStyles(checkBox.getState().inputStyles)) {
  135. StringTokenizer tokenizer = new StringTokenizer(style, " ");
  136. while (tokenizer.hasMoreTokens()) {
  137. checkBox.getState().inputStyles
  138. .remove(tokenizer.nextToken());
  139. }
  140. }
  141. }
  142. }
  143. /**
  144. * The inner label element of the CheckBox.
  145. */
  146. public static class CheckBoxLabelElement implements HasStyleNames {
  147. private final CheckBox checkBox;
  148. private CheckBoxLabelElement(CheckBox checkBox) {
  149. this.checkBox = checkBox;
  150. }
  151. @Override
  152. // Implementation copied from AbstractComponent
  153. public String getStyleName() {
  154. // replaced String with StringBuilder
  155. StringBuilder s = new StringBuilder();
  156. if (ComponentStateUtil
  157. .hasStyles(checkBox.getState(false).labelStyles)) {
  158. for (final Iterator<String> it = checkBox
  159. .getState(false).labelStyles.iterator(); it
  160. .hasNext();) {
  161. s.append(it.next());
  162. if (it.hasNext()) {
  163. s.append(" ");
  164. }
  165. }
  166. }
  167. return s.toString();
  168. }
  169. @Override
  170. // Implementation copied from AbstractComponent
  171. public void setStyleName(String style) {
  172. if (style == null || style.isEmpty()) {
  173. checkBox.getState().labelStyles = null;
  174. return;
  175. }
  176. if (checkBox.getState().labelStyles == null) {
  177. checkBox.getState().labelStyles = new ArrayList<>();
  178. }
  179. List<String> styles = checkBox.getState().labelStyles;
  180. styles.clear();
  181. StringTokenizer tokenizer = new StringTokenizer(style, " ");
  182. while (tokenizer.hasMoreTokens()) {
  183. styles.add(tokenizer.nextToken());
  184. }
  185. }
  186. @Override
  187. // Implementation copied from AbstractComponent
  188. public void addStyleName(String style) {
  189. if (style == null || style.isEmpty()) {
  190. return;
  191. }
  192. if (checkBox.getState().labelStyles != null
  193. && checkBox.getState().labelStyles.contains(style)) {
  194. return;
  195. }
  196. if (style.contains(" ")) {
  197. // Split space separated style names and add them one by one.
  198. StringTokenizer tokenizer = new StringTokenizer(style, " ");
  199. while (tokenizer.hasMoreTokens()) {
  200. addStyleName(tokenizer.nextToken());
  201. }
  202. return;
  203. }
  204. if (checkBox.getState().labelStyles == null) {
  205. checkBox.getState().labelStyles = new ArrayList<>();
  206. }
  207. List<String> styles = checkBox.getState().labelStyles;
  208. styles.add(style);
  209. }
  210. @Override
  211. // Implementation copied from AbstractComponent
  212. public void removeStyleName(String style) {
  213. if (ComponentStateUtil.hasStyles(checkBox.getState().labelStyles)) {
  214. StringTokenizer tokenizer = new StringTokenizer(style, " ");
  215. while (tokenizer.hasMoreTokens()) {
  216. checkBox.getState().labelStyles
  217. .remove(tokenizer.nextToken());
  218. }
  219. }
  220. }
  221. }
  222. /**
  223. * Creates a new checkbox.
  224. */
  225. public CheckBox() {
  226. registerRpc(rpc);
  227. registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
  228. setValue(Boolean.FALSE);
  229. }
  230. /**
  231. * Creates a new checkbox with a set caption.
  232. *
  233. * @param caption
  234. * the Checkbox caption.
  235. */
  236. public CheckBox(String caption) {
  237. this();
  238. setCaption(caption);
  239. }
  240. /**
  241. * Creates a new checkbox with a caption and a set initial state.
  242. *
  243. * @param caption
  244. * the caption of the checkbox
  245. * @param initialState
  246. * the initial state of the checkbox
  247. */
  248. public CheckBox(String caption, boolean initialState) {
  249. this(caption);
  250. setValue(initialState);
  251. }
  252. @Override
  253. public Boolean getValue() {
  254. return getState(false).checked;
  255. }
  256. /**
  257. * Sets the value of this CheckBox. If the new value is not equal to
  258. * {@code getValue()}, fires a {@link ValueChangeEvent}. Throws
  259. * {@code NullPointerException} if the value is null.
  260. *
  261. * @param value
  262. * the new value, not {@code null}
  263. * @throws NullPointerException
  264. * if {@code value} is {@code null}
  265. */
  266. @Override
  267. public void setValue(Boolean value) {
  268. Objects.requireNonNull(value, "CheckBox value must not be null");
  269. super.setValue(value);
  270. }
  271. /**
  272. * Sets the value of this CheckBox. If the new value is not equal to
  273. * {@code getValue()}, fires a {@link ValueChangeEvent}. Throws
  274. * {@code NullPointerException} if the value is null.
  275. *
  276. * @param value
  277. * the new value, not {@code null}
  278. * @param userOriginated
  279. * {@code true} if this event originates from the client,
  280. * {@code false} otherwise.
  281. * @throws NullPointerException
  282. * if {@code value} is {@code null}
  283. */
  284. @Override
  285. protected boolean setValue(Boolean value, boolean userOriginated) {
  286. Objects.requireNonNull(value, "CheckBox value must not be null");
  287. return super.setValue(value, userOriginated);
  288. }
  289. @Override
  290. public Boolean getEmptyValue() {
  291. return false;
  292. }
  293. @Override
  294. protected CheckBoxState getState() {
  295. return (CheckBoxState) super.getState();
  296. }
  297. @Override
  298. protected CheckBoxState getState(boolean markAsDirty) {
  299. return (CheckBoxState) super.getState(markAsDirty);
  300. }
  301. @Override
  302. protected void doSetValue(Boolean value) {
  303. getState().checked = value;
  304. }
  305. @Override
  306. public Registration addBlurListener(BlurListener listener) {
  307. return addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
  308. BlurListener.blurMethod);
  309. }
  310. @Override
  311. public Registration addFocusListener(FocusListener listener) {
  312. return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
  313. FocusListener.focusMethod);
  314. }
  315. /*
  316. * (non-Javadoc)
  317. *
  318. * @see com.vaadin.ui.AbstractField#readDesign(org.jsoup.nodes.Element,
  319. * com.vaadin.ui.declarative.DesignContext)
  320. */
  321. @Override
  322. public void readDesign(Element design, DesignContext designContext) {
  323. super.readDesign(design, designContext);
  324. if (design.hasAttr("checked")) {
  325. this.setValue(DesignAttributeHandler.readAttribute("checked",
  326. design.attributes(), Boolean.class), false);
  327. }
  328. }
  329. /*
  330. * (non-Javadoc)
  331. *
  332. * @see com.vaadin.ui.AbstractField#getCustomAttributes()
  333. */
  334. @Override
  335. protected Collection<String> getCustomAttributes() {
  336. Collection<String> attributes = super.getCustomAttributes();
  337. attributes.add("checked");
  338. return attributes;
  339. }
  340. /*
  341. * (non-Javadoc)
  342. *
  343. * @see com.vaadin.ui.AbstractField#writeDesign(org.jsoup.nodes.Element,
  344. * com.vaadin.ui.declarative.DesignContext)
  345. */
  346. @Override
  347. public void writeDesign(Element design, DesignContext designContext) {
  348. super.writeDesign(design, designContext);
  349. CheckBox def = designContext.getDefaultInstance(this);
  350. Attributes attr = design.attributes();
  351. DesignAttributeHandler.writeAttribute("checked", attr, getValue(),
  352. def.getValue(), Boolean.class, designContext);
  353. }
  354. /**
  355. * Returns the {@link CheckBoxInputElement} element to manipulate the style
  356. * name of the {@code input} element of the {@link CheckBox}.
  357. *
  358. * @since 8.7
  359. * @return the current {@link CheckBoxInputElement}, not {@code null}.
  360. */
  361. public CheckBoxInputElement getInputElement() {
  362. if (checkBoxInputElement == null) {
  363. checkBoxInputElement = new CheckBoxInputElement(this);
  364. }
  365. return checkBoxInputElement;
  366. }
  367. /**
  368. * Returns the {@link CheckBoxLabelElement} element to manipulate the style
  369. * name of the {@code label} element of the {@link CheckBox}.
  370. *
  371. * @since 8.7
  372. * @return the current {@link CheckBoxLabelElement}, not {@code null}.
  373. */
  374. public CheckBoxLabelElement getLabelElement() {
  375. if (checkBoxLabelElement == null) {
  376. checkBoxLabelElement = new CheckBoxLabelElement(this);
  377. }
  378. return checkBoxLabelElement;
  379. }
  380. }