Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

VPopupView.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import java.util.HashSet;
  6. import java.util.Iterator;
  7. import java.util.NoSuchElementException;
  8. import java.util.Set;
  9. import com.google.gwt.event.dom.client.ClickEvent;
  10. import com.google.gwt.event.dom.client.ClickHandler;
  11. import com.google.gwt.event.dom.client.KeyCodes;
  12. import com.google.gwt.event.logical.shared.CloseEvent;
  13. import com.google.gwt.event.logical.shared.CloseHandler;
  14. import com.google.gwt.user.client.DOM;
  15. import com.google.gwt.user.client.Element;
  16. import com.google.gwt.user.client.Event;
  17. import com.google.gwt.user.client.ui.Focusable;
  18. import com.google.gwt.user.client.ui.HTML;
  19. import com.google.gwt.user.client.ui.HasWidgets;
  20. import com.google.gwt.user.client.ui.Label;
  21. import com.google.gwt.user.client.ui.PopupPanel;
  22. import com.google.gwt.user.client.ui.RootPanel;
  23. import com.google.gwt.user.client.ui.Widget;
  24. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  25. import com.vaadin.terminal.gwt.client.ComponentConnector;
  26. import com.vaadin.terminal.gwt.client.Container;
  27. import com.vaadin.terminal.gwt.client.RenderInformation.Size;
  28. import com.vaadin.terminal.gwt.client.RenderSpace;
  29. import com.vaadin.terminal.gwt.client.UIDL;
  30. import com.vaadin.terminal.gwt.client.Util;
  31. import com.vaadin.terminal.gwt.client.VCaptionWrapper;
  32. import com.vaadin.terminal.gwt.client.VTooltip;
  33. import com.vaadin.terminal.gwt.client.ui.richtextarea.VRichTextArea;
  34. public class VPopupView extends HTML implements Container, Iterable<Widget> {
  35. public static final String CLASSNAME = "v-popupview";
  36. /** For server-client communication */
  37. String uidlId;
  38. ApplicationConnection client;
  39. /** This variable helps to communicate popup visibility to the server */
  40. boolean hostPopupVisible;
  41. final CustomPopup popup;
  42. private final Label loading = new Label();
  43. /**
  44. * loading constructor
  45. */
  46. public VPopupView() {
  47. super();
  48. popup = new CustomPopup();
  49. setStyleName(CLASSNAME);
  50. popup.setStyleName(CLASSNAME + "-popup");
  51. loading.setStyleName(CLASSNAME + "-loading");
  52. setHTML("");
  53. popup.setWidget(loading);
  54. // When we click to open the popup...
  55. addClickHandler(new ClickHandler() {
  56. public void onClick(ClickEvent event) {
  57. updateState(true);
  58. }
  59. });
  60. // ..and when we close it
  61. popup.addCloseHandler(new CloseHandler<PopupPanel>() {
  62. public void onClose(CloseEvent<PopupPanel> event) {
  63. updateState(false);
  64. }
  65. });
  66. popup.setAnimationEnabled(true);
  67. sinkEvents(VTooltip.TOOLTIP_EVENTS);
  68. }
  69. /**
  70. * Update popup visibility to server
  71. *
  72. * @param visibility
  73. */
  74. private void updateState(boolean visible) {
  75. // If we know the server connection
  76. // then update the current situation
  77. if (uidlId != null && client != null && isAttached()) {
  78. client.updateVariable(uidlId, "popupVisibility", visible, true);
  79. }
  80. }
  81. void preparePopup(final CustomPopup popup) {
  82. popup.setVisible(false);
  83. popup.show();
  84. }
  85. /**
  86. * Determines the correct position for a popup and displays the popup at
  87. * that position.
  88. *
  89. * By default, the popup is shown centered relative to its host component,
  90. * ensuring it is visible on the screen if possible.
  91. *
  92. * Can be overridden to customize the popup position.
  93. *
  94. * @param popup
  95. */
  96. protected void showPopup(final CustomPopup popup) {
  97. int windowTop = RootPanel.get().getAbsoluteTop();
  98. int windowLeft = RootPanel.get().getAbsoluteLeft();
  99. int windowRight = windowLeft + RootPanel.get().getOffsetWidth();
  100. int windowBottom = windowTop + RootPanel.get().getOffsetHeight();
  101. int offsetWidth = popup.getOffsetWidth();
  102. int offsetHeight = popup.getOffsetHeight();
  103. int hostHorizontalCenter = VPopupView.this.getAbsoluteLeft()
  104. + VPopupView.this.getOffsetWidth() / 2;
  105. int hostVerticalCenter = VPopupView.this.getAbsoluteTop()
  106. + VPopupView.this.getOffsetHeight() / 2;
  107. int left = hostHorizontalCenter - offsetWidth / 2;
  108. int top = hostVerticalCenter - offsetHeight / 2;
  109. // Don't show the popup outside the screen.
  110. if ((left + offsetWidth) > windowRight) {
  111. left -= (left + offsetWidth) - windowRight;
  112. }
  113. if ((top + offsetHeight) > windowBottom) {
  114. top -= (top + offsetHeight) - windowBottom;
  115. }
  116. if (left < 0) {
  117. left = 0;
  118. }
  119. if (top < 0) {
  120. top = 0;
  121. }
  122. popup.setPopupPosition(left, top);
  123. popup.setVisible(true);
  124. }
  125. /**
  126. * Make sure that we remove the popup when the main widget is removed.
  127. *
  128. * @see com.google.gwt.user.client.ui.Widget#onUnload()
  129. */
  130. @Override
  131. protected void onDetach() {
  132. popup.hide();
  133. super.onDetach();
  134. }
  135. private static native void nativeBlur(Element e)
  136. /*-{
  137. if(e && e.blur) {
  138. e.blur();
  139. }
  140. }-*/;
  141. /**
  142. * This class is only protected to enable overriding showPopup, and is
  143. * currently not intended to be extended or otherwise used directly. Its API
  144. * (other than it being a VOverlay) is to be considered private and
  145. * potentially subject to change.
  146. */
  147. protected class CustomPopup extends VOverlay {
  148. private ComponentConnector popupComponentPaintable = null;
  149. Widget popupComponentWidget = null;
  150. VCaptionWrapper captionWrapper = null;
  151. private boolean hasHadMouseOver = false;
  152. private boolean hideOnMouseOut = true;
  153. private final Set<Element> activeChildren = new HashSet<Element>();
  154. private boolean hiding = false;
  155. public CustomPopup() {
  156. super(true, false, true); // autoHide, not modal, dropshadow
  157. }
  158. // For some reason ONMOUSEOUT events are not always received, so we have
  159. // to use ONMOUSEMOVE that doesn't target the popup
  160. @Override
  161. public boolean onEventPreview(Event event) {
  162. Element target = DOM.eventGetTarget(event);
  163. boolean eventTargetsPopup = DOM.isOrHasChild(getElement(), target);
  164. int type = DOM.eventGetType(event);
  165. // Catch children that use keyboard, so we can unfocus them when
  166. // hiding
  167. if (eventTargetsPopup && type == Event.ONKEYPRESS) {
  168. activeChildren.add(target);
  169. }
  170. if (eventTargetsPopup && type == Event.ONMOUSEMOVE) {
  171. hasHadMouseOver = true;
  172. }
  173. if (!eventTargetsPopup && type == Event.ONMOUSEMOVE) {
  174. if (hasHadMouseOver && hideOnMouseOut) {
  175. hide();
  176. return true;
  177. }
  178. }
  179. // Was the TAB key released outside of our popup?
  180. if (!eventTargetsPopup && type == Event.ONKEYUP
  181. && event.getKeyCode() == KeyCodes.KEY_TAB) {
  182. // Should we hide on focus out (mouse out)?
  183. if (hideOnMouseOut) {
  184. hide();
  185. return true;
  186. }
  187. }
  188. return super.onEventPreview(event);
  189. }
  190. @Override
  191. public void hide(boolean autoClosed) {
  192. hiding = true;
  193. syncChildren();
  194. unregisterPaintables();
  195. if (popupComponentWidget != null && popupComponentWidget != loading) {
  196. remove(popupComponentWidget);
  197. }
  198. hasHadMouseOver = false;
  199. super.hide(autoClosed);
  200. }
  201. @Override
  202. public void show() {
  203. hiding = false;
  204. super.show();
  205. }
  206. /**
  207. * Try to sync all known active child widgets to server
  208. */
  209. public void syncChildren() {
  210. // Notify children with focus
  211. if ((popupComponentWidget instanceof Focusable)) {
  212. ((Focusable) popupComponentWidget).setFocus(false);
  213. } else {
  214. checkForRTE(popupComponentWidget);
  215. }
  216. // Notify children that have used the keyboard
  217. for (Element e : activeChildren) {
  218. try {
  219. nativeBlur(e);
  220. } catch (Exception ignored) {
  221. }
  222. }
  223. activeChildren.clear();
  224. }
  225. private void checkForRTE(Widget popupComponentWidget2) {
  226. if (popupComponentWidget2 instanceof VRichTextArea) {
  227. ((VRichTextArea) popupComponentWidget2)
  228. .synchronizeContentToServer();
  229. } else if (popupComponentWidget2 instanceof HasWidgets) {
  230. HasWidgets hw = (HasWidgets) popupComponentWidget2;
  231. Iterator<Widget> iterator = hw.iterator();
  232. while (iterator.hasNext()) {
  233. checkForRTE(iterator.next());
  234. }
  235. }
  236. }
  237. @Override
  238. public boolean remove(Widget w) {
  239. popupComponentPaintable = null;
  240. popupComponentWidget = null;
  241. captionWrapper = null;
  242. return super.remove(w);
  243. }
  244. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  245. ComponentConnector newPopupComponent = client.getPaintable(uidl
  246. .getChildUIDL(0));
  247. if (newPopupComponent != popupComponentPaintable) {
  248. Widget newWidget = newPopupComponent.getWidget();
  249. setWidget(newWidget);
  250. popupComponentWidget = newWidget;
  251. popupComponentPaintable = newPopupComponent;
  252. }
  253. popupComponentPaintable
  254. .updateFromUIDL(uidl.getChildUIDL(0), client);
  255. }
  256. public void unregisterPaintables() {
  257. if (popupComponentPaintable != null) {
  258. client.unregisterPaintable(popupComponentPaintable);
  259. }
  260. }
  261. public void setHideOnMouseOut(boolean hideOnMouseOut) {
  262. this.hideOnMouseOut = hideOnMouseOut;
  263. }
  264. /*
  265. *
  266. * We need a hack make popup act as a child of VPopupView in Vaadin's
  267. * component tree, but work in default GWT manner when closing or
  268. * opening.
  269. *
  270. * (non-Javadoc)
  271. *
  272. * @see com.google.gwt.user.client.ui.Widget#getParent()
  273. */
  274. @Override
  275. public Widget getParent() {
  276. if (!isAttached() || hiding) {
  277. return super.getParent();
  278. } else {
  279. return VPopupView.this;
  280. }
  281. }
  282. @Override
  283. protected void onDetach() {
  284. super.onDetach();
  285. hiding = false;
  286. }
  287. @Override
  288. public Element getContainerElement() {
  289. return super.getContainerElement();
  290. }
  291. }// class CustomPopup
  292. // Container methods
  293. public RenderSpace getAllocatedSpace(Widget child) {
  294. Size popupExtra = calculatePopupExtra();
  295. return new RenderSpace(RootPanel.get().getOffsetWidth()
  296. - popupExtra.getWidth(), RootPanel.get().getOffsetHeight()
  297. - popupExtra.getHeight());
  298. }
  299. /**
  300. * Calculate extra space taken by the popup decorations
  301. *
  302. * @return
  303. */
  304. protected Size calculatePopupExtra() {
  305. Element pe = popup.getElement();
  306. Element ipe = popup.getContainerElement();
  307. // border + padding
  308. int width = Util.getRequiredWidth(pe) - Util.getRequiredWidth(ipe);
  309. int height = Util.getRequiredHeight(pe) - Util.getRequiredHeight(ipe);
  310. return new Size(width, height);
  311. }
  312. public boolean hasChildComponent(Widget component) {
  313. if (popup.popupComponentWidget != null) {
  314. return popup.popupComponentWidget == component;
  315. } else {
  316. return false;
  317. }
  318. }
  319. public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
  320. popup.setWidget(newComponent);
  321. popup.popupComponentWidget = newComponent;
  322. }
  323. public boolean requestLayout(Set<Widget> children) {
  324. popup.updateShadowSizeAndPosition();
  325. return true;
  326. }
  327. @Override
  328. public void onBrowserEvent(Event event) {
  329. super.onBrowserEvent(event);
  330. if (client != null) {
  331. client.handleTooltipEvent(event, this);
  332. }
  333. }
  334. public Iterator<Widget> iterator() {
  335. return new Iterator<Widget>() {
  336. int pos = 0;
  337. public boolean hasNext() {
  338. // There is a child widget only if next() has not been called.
  339. return (pos == 0);
  340. }
  341. public Widget next() {
  342. // Next can be called only once to return the popup.
  343. if (pos != 0) {
  344. throw new NoSuchElementException();
  345. }
  346. pos++;
  347. return popup;
  348. }
  349. public void remove() {
  350. throw new UnsupportedOperationException();
  351. }
  352. };
  353. }
  354. }// class VPopupView