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.

PopupView.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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.io.Serializable;
  18. import java.lang.reflect.Method;
  19. import java.util.Collections;
  20. import java.util.Iterator;
  21. import org.jsoup.nodes.Element;
  22. import org.jsoup.nodes.Node;
  23. import org.jsoup.parser.Tag;
  24. import com.vaadin.shared.Registration;
  25. import com.vaadin.shared.ui.popupview.PopupViewServerRpc;
  26. import com.vaadin.shared.ui.popupview.PopupViewState;
  27. import com.vaadin.ui.declarative.DesignContext;
  28. /**
  29. *
  30. * A component for displaying a two different views to data. The minimized view
  31. * is normally used to render the component, and when it is clicked the full
  32. * view is displayed on a popup. The inner class {@link PopupView.Content} is
  33. * used to deliver contents to this component.
  34. *
  35. * @author Vaadin Ltd.
  36. */
  37. @SuppressWarnings("serial")
  38. public class PopupView extends AbstractComponent implements HasComponents {
  39. private Content content;
  40. private Component visibleComponent;
  41. private static final Method POPUP_VISIBILITY_METHOD;
  42. static {
  43. try {
  44. POPUP_VISIBILITY_METHOD = PopupVisibilityListener.class
  45. .getDeclaredMethod("popupVisibilityChange",
  46. PopupVisibilityEvent.class);
  47. } catch (final java.lang.NoSuchMethodException e) {
  48. // This should never happen
  49. throw new java.lang.RuntimeException(
  50. "Internal error finding methods in PopupView");
  51. }
  52. }
  53. private final PopupViewServerRpc rpc = this::setPopupVisible;
  54. /* Constructors */
  55. /**
  56. * This is an internal constructor. Use
  57. * {@link PopupView#PopupView(String, Component)} instead.
  58. *
  59. * @since 7.5.0
  60. */
  61. @Deprecated
  62. public PopupView() {
  63. registerRpc(rpc);
  64. setHideOnMouseOut(true);
  65. setContent(createContent("", new Label("")));
  66. }
  67. /**
  68. * A simple way to create a PopupPanel. Note that the minimal representation
  69. * may not be dynamically updated, in order to achieve this create your own
  70. * Content object and use {@link PopupView#PopupView(Content)}.
  71. *
  72. * @param small
  73. * the minimal textual representation as HTML
  74. * @param large
  75. * the full, Component-type representation
  76. */
  77. public PopupView(final java.lang.String small, final Component large) {
  78. this(createContent(small, large));
  79. }
  80. /**
  81. * Creates a PopupView through the PopupView.Content interface. This allows
  82. * the creator to dynamically change the contents of the PopupView.
  83. *
  84. * @param content
  85. * the PopupView.Content that contains the information for this
  86. */
  87. public PopupView(PopupView.Content content) {
  88. this();
  89. setContent(content);
  90. }
  91. /**
  92. * Creates a Content from given text representation and popup content.
  93. *
  94. * @since 7.5.0
  95. *
  96. * @param minimizedValue
  97. * text representation when popup is hidden
  98. * @param popupContent
  99. * popup content
  100. * @return content with given data
  101. */
  102. protected static Content createContent(final String minimizedValue,
  103. final Component popupContent) {
  104. return new Content() {
  105. @Override
  106. public String getMinimizedValueAsHTML() {
  107. return minimizedValue;
  108. }
  109. @Override
  110. public Component getPopupComponent() {
  111. return popupContent;
  112. }
  113. };
  114. }
  115. /**
  116. * This method will replace the current content of the panel with a new one.
  117. *
  118. * @param newContent
  119. * PopupView.Content object containing new information for the
  120. * PopupView
  121. * @throws IllegalArgumentException
  122. * if the method is passed a null value, or if one of the
  123. * content methods returns null
  124. */
  125. public void setContent(PopupView.Content newContent)
  126. throws IllegalArgumentException {
  127. if (newContent == null) {
  128. throw new IllegalArgumentException("Content must not be null");
  129. }
  130. content = newContent;
  131. markAsDirty();
  132. }
  133. /**
  134. * Returns the content-package for this PopupView.
  135. *
  136. * @return the PopupView.Content for this object or null
  137. */
  138. public PopupView.Content getContent() {
  139. return content;
  140. }
  141. /**
  142. * Set the visibility of the popup. Does not hide the minimal
  143. * representation.
  144. *
  145. * @param visible
  146. */
  147. public void setPopupVisible(boolean visible) {
  148. if (isPopupVisible() != visible) {
  149. if (visible) {
  150. visibleComponent = content.getPopupComponent();
  151. if (visibleComponent == null) {
  152. throw new java.lang.IllegalStateException(
  153. "PopupView.Content did not return Component to set visible");
  154. }
  155. if (visibleComponent.getParent() != null) {
  156. // If the component already has a parent, try to remove it
  157. AbstractSingleComponentContainer
  158. .removeFromParent(visibleComponent);
  159. }
  160. visibleComponent.setParent(this);
  161. } else {
  162. if (equals(visibleComponent.getParent())) {
  163. visibleComponent.setParent(null);
  164. }
  165. visibleComponent = null;
  166. }
  167. fireEvent(new PopupVisibilityEvent(this));
  168. markAsDirty();
  169. }
  170. }
  171. @Override
  172. public void beforeClientResponse(boolean initial) {
  173. super.beforeClientResponse(initial);
  174. String html = content.getMinimizedValueAsHTML();
  175. if (html == null) {
  176. html = "";
  177. }
  178. getState().html = html;
  179. }
  180. /**
  181. * Return whether the popup is visible.
  182. *
  183. * @return true if the popup is showing
  184. */
  185. public boolean isPopupVisible() {
  186. return visibleComponent != null;
  187. }
  188. /**
  189. * Check if this popup will be hidden when the user takes the mouse cursor
  190. * out of the popup area.
  191. *
  192. * @return true if the popup is hidden on mouse out, false otherwise
  193. */
  194. public boolean isHideOnMouseOut() {
  195. return getState(false).hideOnMouseOut;
  196. }
  197. /**
  198. * Should the popup automatically hide when the user takes the mouse cursor
  199. * out of the popup area? If this is false, the user must click outside the
  200. * popup to close it. The default is true.
  201. *
  202. * @param hideOnMouseOut
  203. *
  204. */
  205. public void setHideOnMouseOut(boolean hideOnMouseOut) {
  206. getState().hideOnMouseOut = hideOnMouseOut;
  207. }
  208. /*
  209. * Methods inherited from AbstractComponentContainer. These are unnecessary
  210. * (but mandatory). Most of them are not supported in this implementation.
  211. */
  212. /**
  213. * This class only contains other components when the popup is showing.
  214. *
  215. * @see com.vaadin.ui.ComponentContainer#getComponentIterator()
  216. */
  217. @Override
  218. public Iterator<Component> iterator() {
  219. if (visibleComponent != null) {
  220. return Collections.singletonList(visibleComponent).iterator();
  221. } else {
  222. return Collections.<Component> emptyList().iterator();
  223. }
  224. }
  225. /**
  226. * Gets the number of contained components. Consistent with the iterator
  227. * returned by {@link #getComponentIterator()}.
  228. *
  229. * @return the number of contained components (zero or one)
  230. */
  231. public int getComponentCount() {
  232. return (visibleComponent != null ? 1 : 0);
  233. }
  234. @Override
  235. public void readDesign(Element design, DesignContext designContext) {
  236. // Read content first to avoid NPE when setting popup visible
  237. Component popupContent = null;
  238. String minimizedValue = "";
  239. for (Node childNode : design.childNodes()) {
  240. if (childNode instanceof Element) {
  241. Element child = (Element) childNode;
  242. if (child.tagName().equals("popup-content")) {
  243. popupContent = designContext.readDesign(child.child(0));
  244. } else {
  245. minimizedValue += child.toString();
  246. }
  247. } else {
  248. minimizedValue += childNode.toString();
  249. }
  250. }
  251. setContent(createContent(minimizedValue.trim(), popupContent));
  252. super.readDesign(design, designContext);
  253. }
  254. @Override
  255. public void writeDesign(Element design, DesignContext designContext) {
  256. super.writeDesign(design, designContext);
  257. Element popupContent = new Element(Tag.valueOf("popup-content"), "");
  258. popupContent.appendChild(
  259. designContext.createElement(content.getPopupComponent()));
  260. String minimizedHTML = content.getMinimizedValueAsHTML();
  261. if (minimizedHTML != null && !minimizedHTML.isEmpty()) {
  262. design.append(minimizedHTML);
  263. }
  264. design.appendChild(popupContent);
  265. }
  266. @Override
  267. protected PopupViewState getState() {
  268. return (PopupViewState) super.getState();
  269. }
  270. @Override
  271. protected PopupViewState getState(boolean markAsDirty) {
  272. return (PopupViewState) super.getState(markAsDirty);
  273. }
  274. /**
  275. * Used to deliver customized content-packages to the PopupView. These are
  276. * dynamically loaded when they are redrawn. The user must take care that
  277. * neither of these methods ever return null.
  278. */
  279. public interface Content extends Serializable {
  280. /**
  281. * This should return a small view of the full data.
  282. *
  283. * @return value in HTML format
  284. */
  285. public String getMinimizedValueAsHTML();
  286. /**
  287. * This should return the full Component representing the data
  288. *
  289. * @return a Component for the value
  290. */
  291. public Component getPopupComponent();
  292. }
  293. /**
  294. * Add a listener that is called whenever the visibility of the popup is
  295. * changed.
  296. *
  297. * @see PopupVisibilityListener
  298. * @see PopupVisibilityEvent
  299. *
  300. * @param listener
  301. * the listener to add, not null
  302. * @return a registration object for removing the listener
  303. */
  304. public Registration addPopupVisibilityListener(
  305. PopupVisibilityListener listener) {
  306. return addListener(PopupVisibilityEvent.class, listener,
  307. POPUP_VISIBILITY_METHOD);
  308. }
  309. /**
  310. * Removes a previously added listener, so that it no longer receives events
  311. * when the visibility of the popup changes.
  312. *
  313. * @param listener
  314. * the listener to remove
  315. * @see PopupVisibilityListener
  316. * @see #addListener(PopupVisibilityListener)
  317. *
  318. * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the
  319. * registration object returned from
  320. * {@link #addPopupVisibilityListener(PopupVisibilityListener)}.
  321. */
  322. @Deprecated
  323. public void removePopupVisibilityListener(
  324. PopupVisibilityListener listener) {
  325. removeListener(PopupVisibilityEvent.class, listener,
  326. POPUP_VISIBILITY_METHOD);
  327. }
  328. /**
  329. * This event is received by the PopupVisibilityListeners when the
  330. * visibility of the popup changes. You can get the new visibility directly
  331. * with {@link #isPopupVisible()}, or get the PopupView that produced the
  332. * event with {@link #getPopupView()}.
  333. *
  334. */
  335. public static class PopupVisibilityEvent extends Event {
  336. public PopupVisibilityEvent(PopupView source) {
  337. super(source);
  338. }
  339. /**
  340. * Get the PopupView instance that is the source of this event.
  341. *
  342. * @return the source PopupView
  343. */
  344. public PopupView getPopupView() {
  345. return (PopupView) getSource();
  346. }
  347. /**
  348. * Returns the current visibility of the popup.
  349. *
  350. * @return true if the popup is visible
  351. */
  352. public boolean isPopupVisible() {
  353. return getPopupView().isPopupVisible();
  354. }
  355. }
  356. /**
  357. * Defines a listener that can receive a PopupVisibilityEvent when the
  358. * visibility of the popup changes.
  359. *
  360. */
  361. public interface PopupVisibilityListener extends Serializable {
  362. /**
  363. * Pass to {@link PopupView#PopupVisibilityEvent} to start listening for
  364. * popup visibility changes.
  365. *
  366. * @param event
  367. * the event
  368. *
  369. * @see PopupVisibilityEvent
  370. * @see PopupView#addListener(PopupVisibilityListener)
  371. */
  372. public void popupVisibilityChange(PopupVisibilityEvent event);
  373. }
  374. }