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.

VTooltip.java 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client;
  5. import com.google.gwt.user.client.DOM;
  6. import com.google.gwt.user.client.Element;
  7. import com.google.gwt.user.client.Event;
  8. import com.google.gwt.user.client.Timer;
  9. import com.google.gwt.user.client.Window;
  10. import com.google.gwt.user.client.ui.FlowPanel;
  11. import com.vaadin.terminal.gwt.client.ui.VOverlay;
  12. /**
  13. * TODO open for extension
  14. */
  15. public class VTooltip extends VOverlay {
  16. private static final String CLASSNAME = "v-tooltip";
  17. private static final int MARGIN = 4;
  18. public static final int TOOLTIP_EVENTS = Event.ONKEYDOWN
  19. | Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONMOUSEMOVE
  20. | Event.ONCLICK;
  21. protected static final int MAX_WIDTH = 500;
  22. private static final int QUICK_OPEN_TIMEOUT = 1000;
  23. private static final int CLOSE_TIMEOUT = 300;
  24. private static final int OPEN_DELAY = 750;
  25. private static final int QUICK_OPEN_DELAY = 100;
  26. VErrorMessage em = new VErrorMessage();
  27. Element description = DOM.createDiv();
  28. private ComponentConnector tooltipOwner;
  29. private boolean closing = false;
  30. private boolean opening = false;
  31. private ApplicationConnection ac;
  32. // Open next tooltip faster. Disabled after 2 sec of showTooltip-silence.
  33. private boolean justClosed = false;
  34. // If this is "additional" tooltip, this field contains the key for it
  35. private Object tooltipKey;
  36. public VTooltip(ApplicationConnection client) {
  37. super(false, false, true);
  38. ac = client;
  39. setStyleName(CLASSNAME);
  40. FlowPanel layout = new FlowPanel();
  41. setWidget(layout);
  42. layout.add(em);
  43. DOM.setElementProperty(description, "className", CLASSNAME + "-text");
  44. DOM.appendChild(layout.getElement(), description);
  45. setSinkShadowEvents(true);
  46. }
  47. /**
  48. * Show a popup containing the information in the "info" tooltip
  49. *
  50. * @param info
  51. */
  52. private void show(TooltipInfo info) {
  53. boolean hasContent = false;
  54. if (info.getErrorUidl() != null) {
  55. em.setVisible(true);
  56. em.updateFromUIDL(info.getErrorUidl());
  57. hasContent = true;
  58. } else {
  59. em.setVisible(false);
  60. }
  61. if (info.getTitle() != null && !"".equals(info.getTitle())) {
  62. DOM.setInnerHTML(description, info.getTitle());
  63. DOM.setStyleAttribute(description, "display", "");
  64. hasContent = true;
  65. } else {
  66. DOM.setInnerHTML(description, "");
  67. DOM.setStyleAttribute(description, "display", "none");
  68. }
  69. if (hasContent) {
  70. setPopupPositionAndShow(new PositionCallback() {
  71. public void setPosition(int offsetWidth, int offsetHeight) {
  72. if (offsetWidth > MAX_WIDTH) {
  73. setWidth(MAX_WIDTH + "px");
  74. // Check new height and width with reflowed content
  75. offsetWidth = getOffsetWidth();
  76. offsetHeight = getOffsetHeight();
  77. }
  78. int x = tooltipEventMouseX + 10 + Window.getScrollLeft();
  79. int y = tooltipEventMouseY + 10 + Window.getScrollTop();
  80. if (x + offsetWidth + MARGIN - Window.getScrollLeft() > Window
  81. .getClientWidth()) {
  82. x = Window.getClientWidth() - offsetWidth - MARGIN;
  83. }
  84. if (y + offsetHeight + MARGIN - Window.getScrollTop() > Window
  85. .getClientHeight()) {
  86. y = tooltipEventMouseY - 5 - offsetHeight;
  87. if (y - Window.getScrollTop() < 0) {
  88. // tooltip does not fit on top of the mouse either,
  89. // put it at the top of the screen
  90. y = Window.getScrollTop();
  91. }
  92. }
  93. setPopupPosition(x, y);
  94. sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT);
  95. }
  96. });
  97. } else {
  98. hide();
  99. }
  100. }
  101. public void showTooltip(ComponentConnector owner, Event event, Object key) {
  102. if (closing && tooltipOwner == owner && tooltipKey == key) {
  103. // return to same tooltip, cancel closing
  104. closeTimer.cancel();
  105. closing = false;
  106. justClosedTimer.cancel();
  107. justClosed = false;
  108. return;
  109. }
  110. if (closing) {
  111. closeNow();
  112. }
  113. updatePosition(event);
  114. if (opening) {
  115. showTimer.cancel();
  116. }
  117. tooltipOwner = owner;
  118. tooltipKey = key;
  119. // Schedule timer for showing the tooltip according to if it was
  120. // recently closed or not.
  121. if (justClosed) {
  122. showTimer.schedule(QUICK_OPEN_DELAY);
  123. } else {
  124. showTimer.schedule(OPEN_DELAY);
  125. }
  126. opening = true;
  127. }
  128. private void closeNow() {
  129. if (closing) {
  130. hide();
  131. tooltipOwner = null;
  132. setWidth("");
  133. closing = false;
  134. }
  135. }
  136. private Timer showTimer = new Timer() {
  137. @Override
  138. public void run() {
  139. TooltipInfo info = ac.getTooltipTitleInfo(tooltipOwner, tooltipKey);
  140. if (null != info) {
  141. show(info);
  142. }
  143. opening = false;
  144. }
  145. };
  146. private Timer closeTimer = new Timer() {
  147. @Override
  148. public void run() {
  149. closeNow();
  150. justClosedTimer.schedule(2000);
  151. justClosed = true;
  152. }
  153. };
  154. private Timer justClosedTimer = new Timer() {
  155. @Override
  156. public void run() {
  157. justClosed = false;
  158. }
  159. };
  160. public void hideTooltip() {
  161. if (opening) {
  162. showTimer.cancel();
  163. opening = false;
  164. tooltipOwner = null;
  165. }
  166. if (!isAttached()) {
  167. return;
  168. }
  169. if (closing) {
  170. // already about to close
  171. return;
  172. }
  173. closeTimer.schedule(CLOSE_TIMEOUT);
  174. closing = true;
  175. justClosed = true;
  176. justClosedTimer.schedule(QUICK_OPEN_TIMEOUT);
  177. }
  178. private int tooltipEventMouseX;
  179. private int tooltipEventMouseY;
  180. public void updatePosition(Event event) {
  181. tooltipEventMouseX = DOM.eventGetClientX(event);
  182. tooltipEventMouseY = DOM.eventGetClientY(event);
  183. }
  184. public void handleTooltipEvent(Event event, ComponentConnector owner, Object key) {
  185. final int type = DOM.eventGetType(event);
  186. if ((VTooltip.TOOLTIP_EVENTS & type) == type) {
  187. if (type == Event.ONMOUSEOVER) {
  188. showTooltip(owner, event, key);
  189. } else if (type == Event.ONMOUSEMOVE) {
  190. updatePosition(event);
  191. } else {
  192. hideTooltip();
  193. }
  194. } else {
  195. // non-tooltip event, hide tooltip
  196. hideTooltip();
  197. }
  198. }
  199. @Override
  200. public void onBrowserEvent(Event event) {
  201. final int type = DOM.eventGetType(event);
  202. // cancel closing event if tooltip is mouseovered; the user might want
  203. // to scroll of cut&paste
  204. switch (type) {
  205. case Event.ONMOUSEOVER:
  206. closeTimer.cancel();
  207. closing = false;
  208. break;
  209. case Event.ONMOUSEOUT:
  210. hideTooltip();
  211. break;
  212. default:
  213. // NOP
  214. }
  215. }
  216. }