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.

VPopupView.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. package com.vaadin.terminal.gwt.client.ui;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. import com.google.gwt.user.client.DOM;
  5. import com.google.gwt.user.client.Element;
  6. import com.google.gwt.user.client.Event;
  7. import com.google.gwt.user.client.ui.ClickListener;
  8. import com.google.gwt.user.client.ui.HTML;
  9. import com.google.gwt.user.client.ui.HasFocus;
  10. import com.google.gwt.user.client.ui.Label;
  11. import com.google.gwt.user.client.ui.PopupListener;
  12. import com.google.gwt.user.client.ui.PopupPanel;
  13. import com.google.gwt.user.client.ui.RootPanel;
  14. import com.google.gwt.user.client.ui.Widget;
  15. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  16. import com.vaadin.terminal.gwt.client.Container;
  17. import com.vaadin.terminal.gwt.client.VCaption;
  18. import com.vaadin.terminal.gwt.client.VCaptionWrapper;
  19. import com.vaadin.terminal.gwt.client.VTooltip;
  20. import com.vaadin.terminal.gwt.client.Paintable;
  21. import com.vaadin.terminal.gwt.client.RenderSpace;
  22. import com.vaadin.terminal.gwt.client.UIDL;
  23. import com.vaadin.terminal.gwt.client.Util;
  24. import com.vaadin.terminal.gwt.client.RenderInformation.Size;
  25. public class VPopupView extends HTML implements Container {
  26. public static final String CLASSNAME = "i-popupview";
  27. /** For server-client communication */
  28. private String uidlId;
  29. private ApplicationConnection client;
  30. /** This variable helps to communicate popup visibility to the server */
  31. private boolean hostPopupVisible;
  32. private final CustomPopup popup;
  33. private final Label loading = new Label("Loading...");
  34. /**
  35. * loading constructor
  36. */
  37. public VPopupView() {
  38. super();
  39. popup = new CustomPopup();
  40. setStyleName(CLASSNAME);
  41. popup.setStylePrimaryName(CLASSNAME + "-popup");
  42. setHTML("(No HTML defined for PopupView)");
  43. popup.setWidget(loading);
  44. // When we click to open the popup...
  45. addClickListener(new ClickListener() {
  46. public void onClick(Widget sender) {
  47. updateState(true);
  48. }
  49. });
  50. // ..and when we close it
  51. popup.addPopupListener(new PopupListener() {
  52. public void onPopupClosed(PopupPanel sender, boolean autoClosed) {
  53. updateState(false);
  54. }
  55. });
  56. popup.setAnimationEnabled(true);
  57. sinkEvents(VTooltip.TOOLTIP_EVENTS);
  58. }
  59. /**
  60. *
  61. *
  62. * @see com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal.gwt.client.UIDL,
  63. * com.vaadin.terminal.gwt.client.ApplicationConnection)
  64. */
  65. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  66. // This call should be made first. Ensure correct implementation,
  67. // and don't let the containing layout manage caption.
  68. if (client.updateComponent(this, uidl, false)) {
  69. return;
  70. }
  71. // These are for future server connections
  72. this.client = client;
  73. uidlId = uidl.getId();
  74. hostPopupVisible = uidl.getBooleanVariable("popupVisibility");
  75. setHTML(uidl.getStringAttribute("html"));
  76. if (uidl.hasAttribute("hideOnMouseOut")) {
  77. popup.setHideOnMouseOut(uidl.getBooleanAttribute("hideOnMouseOut"));
  78. }
  79. // Render the popup if visible and show it.
  80. if (hostPopupVisible) {
  81. UIDL popupUIDL = uidl.getChildUIDL(0);
  82. // showPopupOnTop(popup, hostReference);
  83. preparePopup(popup);
  84. popup.updateFromUIDL(popupUIDL, client);
  85. if (uidl.hasAttribute("style")) {
  86. final String[] styles = uidl.getStringAttribute("style").split(
  87. " ");
  88. final StringBuffer styleBuf = new StringBuffer();
  89. final String primaryName = popup.getStylePrimaryName();
  90. styleBuf.append(primaryName);
  91. for (int i = 0; i < styles.length; i++) {
  92. styleBuf.append(" ");
  93. styleBuf.append(primaryName);
  94. styleBuf.append("-");
  95. styleBuf.append(styles[i]);
  96. }
  97. popup.setStyleName(styleBuf.toString());
  98. } else {
  99. popup.setStyleName(popup.getStylePrimaryName());
  100. }
  101. showPopup(popup);
  102. // The popup shouldn't be visible, try to hide it.
  103. } else {
  104. popup.hide();
  105. }
  106. }// updateFromUIDL
  107. /**
  108. * Update popup visibility to server
  109. *
  110. * @param visibility
  111. */
  112. private void updateState(boolean visible) {
  113. // If we know the server connection
  114. // then update the current situation
  115. if (uidlId != null && client != null && isAttached()) {
  116. client.updateVariable(uidlId, "popupVisibility", visible, true);
  117. }
  118. }
  119. private void preparePopup(final CustomPopup popup) {
  120. popup.setVisible(false);
  121. popup.show();
  122. }
  123. private void showPopup(final CustomPopup popup) {
  124. int windowTop = RootPanel.get().getAbsoluteTop();
  125. int windowLeft = RootPanel.get().getAbsoluteLeft();
  126. int windowRight = windowLeft + RootPanel.get().getOffsetWidth();
  127. int windowBottom = windowTop + RootPanel.get().getOffsetHeight();
  128. int offsetWidth = popup.getOffsetWidth();
  129. int offsetHeight = popup.getOffsetHeight();
  130. int hostHorizontalCenter = VPopupView.this.getAbsoluteLeft()
  131. + VPopupView.this.getOffsetWidth() / 2;
  132. int hostVerticalCenter = VPopupView.this.getAbsoluteTop()
  133. + VPopupView.this.getOffsetHeight() / 2;
  134. int left = hostHorizontalCenter - offsetWidth / 2;
  135. int top = hostVerticalCenter - offsetHeight / 2;
  136. // Superclass takes care of top and left
  137. if ((left + offsetWidth) > windowRight) {
  138. left -= (left + offsetWidth) - windowRight;
  139. }
  140. if ((top + offsetHeight) > windowBottom) {
  141. top -= (top + offsetHeight) - windowBottom;
  142. }
  143. popup.setPopupPosition(left, top);
  144. popup.setVisible(true);
  145. }
  146. /**
  147. * Make sure that we remove the popup when the main widget is removed.
  148. *
  149. * @see com.google.gwt.user.client.ui.Widget#onUnload()
  150. */
  151. @Override
  152. protected void onDetach() {
  153. popup.hide();
  154. super.onDetach();
  155. }
  156. private static native void nativeBlur(Element e)
  157. /*-{
  158. if(e && e.blur) {
  159. e.blur();
  160. }
  161. }-*/;
  162. private class CustomPopup extends VToolkitOverlay {
  163. private Paintable popupComponentPaintable = null;
  164. private Widget popupComponentWidget = null;
  165. private VCaptionWrapper captionWrapper = null;
  166. private boolean hasHadMouseOver = false;
  167. private boolean hideOnMouseOut = true;
  168. private final Set<Element> activeChildren = new HashSet<Element>();
  169. private boolean hiding = false;
  170. public CustomPopup() {
  171. super(true, false, true); // autoHide, not modal, dropshadow
  172. }
  173. // For some reason ONMOUSEOUT events are not always received, so we have
  174. // to use ONMOUSEMOVE that doesn't target the popup
  175. @Override
  176. public boolean onEventPreview(Event event) {
  177. Element target = DOM.eventGetTarget(event);
  178. boolean eventTargetsPopup = DOM.isOrHasChild(getElement(), target);
  179. int type = DOM.eventGetType(event);
  180. // Catch children that use keyboard, so we can unfocus them when
  181. // hiding
  182. if (eventTargetsPopup && type == Event.ONKEYPRESS) {
  183. activeChildren.add(target);
  184. }
  185. if (eventTargetsPopup && type == Event.ONMOUSEMOVE) {
  186. hasHadMouseOver = true;
  187. }
  188. if (!eventTargetsPopup && type == Event.ONMOUSEMOVE) {
  189. if (hasHadMouseOver && hideOnMouseOut) {
  190. hide();
  191. return true;
  192. }
  193. }
  194. return super.onEventPreview(event);
  195. }
  196. @Override
  197. public void hide(boolean autoClosed) {
  198. hiding = true;
  199. syncChildren();
  200. unregisterPaintables();
  201. if (popupComponentWidget != null && popupComponentWidget != loading) {
  202. remove(popupComponentWidget);
  203. }
  204. hasHadMouseOver = false;
  205. super.hide(autoClosed);
  206. }
  207. @Override
  208. public void show() {
  209. hiding = false;
  210. super.show();
  211. }
  212. /**
  213. * Try to sync all known active child widgets to server
  214. */
  215. public void syncChildren() {
  216. // Notify children with focus
  217. if ((popupComponentWidget instanceof HasFocus)) {
  218. ((HasFocus) popupComponentWidget).setFocus(false);
  219. }
  220. // Notify children that have used the keyboard
  221. for (Element e : activeChildren) {
  222. try {
  223. nativeBlur(e);
  224. } catch (Exception ignored) {
  225. }
  226. }
  227. activeChildren.clear();
  228. }
  229. @Override
  230. public boolean remove(Widget w) {
  231. popupComponentPaintable = null;
  232. popupComponentWidget = null;
  233. captionWrapper = null;
  234. return super.remove(w);
  235. }
  236. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  237. Paintable newPopupComponent = client.getPaintable(uidl
  238. .getChildUIDL(0));
  239. if (newPopupComponent != popupComponentPaintable) {
  240. setWidget((Widget) newPopupComponent);
  241. popupComponentWidget = (Widget) newPopupComponent;
  242. popupComponentPaintable = newPopupComponent;
  243. }
  244. popupComponentPaintable
  245. .updateFromUIDL(uidl.getChildUIDL(0), client);
  246. }
  247. public void unregisterPaintables() {
  248. if (popupComponentPaintable != null) {
  249. client.unregisterPaintable(popupComponentPaintable);
  250. }
  251. }
  252. public void setHideOnMouseOut(boolean hideOnMouseOut) {
  253. this.hideOnMouseOut = hideOnMouseOut;
  254. }
  255. /*
  256. *
  257. * We need a hack make popup act as a child of VPopupView in toolkits
  258. * component tree, but work in default GWT manner when closing or
  259. * opening.
  260. *
  261. * (non-Javadoc)
  262. *
  263. * @see com.google.gwt.user.client.ui.Widget#getParent()
  264. */
  265. @Override
  266. public Widget getParent() {
  267. if (!isAttached() || hiding) {
  268. return super.getParent();
  269. } else {
  270. return VPopupView.this;
  271. }
  272. }
  273. @Override
  274. protected void onDetach() {
  275. super.onDetach();
  276. hiding = false;
  277. }
  278. @Override
  279. public Element getContainerElement() {
  280. return super.getContainerElement();
  281. }
  282. }// class CustomPopup
  283. // Container methods
  284. public RenderSpace getAllocatedSpace(Widget child) {
  285. Size popupExtra = calculatePopupExtra();
  286. return new RenderSpace(RootPanel.get().getOffsetWidth()
  287. - popupExtra.getWidth(), RootPanel.get().getOffsetHeight()
  288. - popupExtra.getHeight());
  289. }
  290. /**
  291. * Calculate extra space taken by the popup decorations
  292. *
  293. * @return
  294. */
  295. protected Size calculatePopupExtra() {
  296. Element pe = popup.getElement();
  297. Element ipe = popup.getContainerElement();
  298. // border + padding
  299. int width = Util.getRequiredWidth(pe) - Util.getRequiredWidth(ipe);
  300. int height = Util.getRequiredHeight(pe) - Util.getRequiredHeight(ipe);
  301. return new Size(width, height);
  302. }
  303. public boolean hasChildComponent(Widget component) {
  304. if (popup.popupComponentWidget != null) {
  305. return popup.popupComponentWidget == component;
  306. } else {
  307. return false;
  308. }
  309. }
  310. public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
  311. popup.setWidget(newComponent);
  312. popup.popupComponentWidget = newComponent;
  313. }
  314. public boolean requestLayout(Set<Paintable> child) {
  315. return true;
  316. }
  317. public void updateCaption(Paintable component, UIDL uidl) {
  318. if (VCaption.isNeeded(uidl)) {
  319. if (popup.captionWrapper != null) {
  320. popup.captionWrapper.updateCaption(uidl);
  321. } else {
  322. popup.captionWrapper = new VCaptionWrapper(component, client);
  323. popup.setWidget(popup.captionWrapper);
  324. popup.captionWrapper.updateCaption(uidl);
  325. }
  326. } else {
  327. if (popup.captionWrapper != null) {
  328. popup.setWidget(popup.popupComponentWidget);
  329. }
  330. }
  331. popup.popupComponentWidget = (Widget) component;
  332. popup.popupComponentPaintable = component;
  333. }
  334. @Override
  335. public void onBrowserEvent(Event event) {
  336. super.onBrowserEvent(event);
  337. if (client != null) {
  338. client.handleTooltipEvent(event, this);
  339. }
  340. }
  341. }// class VPopupView