Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

VPopupView.java 16KB

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