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.

VOverlay.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import com.google.gwt.animation.client.Animation;
  6. import com.google.gwt.dom.client.Document;
  7. import com.google.gwt.event.logical.shared.CloseEvent;
  8. import com.google.gwt.event.logical.shared.CloseHandler;
  9. import com.google.gwt.user.client.DOM;
  10. import com.google.gwt.user.client.Element;
  11. import com.google.gwt.user.client.ui.PopupPanel;
  12. import com.google.gwt.user.client.ui.RootPanel;
  13. import com.vaadin.terminal.gwt.client.BrowserInfo;
  14. import com.vaadin.terminal.gwt.client.Util;
  15. /**
  16. * In Vaadin UI this Overlay should always be used for all elements that
  17. * temporary float over other components like context menus etc. This is to deal
  18. * stacking order correctly with VWindow objects.
  19. */
  20. public class VOverlay extends PopupPanel {
  21. /*
  22. * The z-index value from where all overlays live. This can be overridden in
  23. * any extending class.
  24. */
  25. protected static int Z_INDEX = 20000;
  26. /*
  27. * Shadow element style. If an extending class wishes to use a different
  28. * style of shadow, it can use setShadowStyle(String) to give the shadow
  29. * element a new style name.
  30. */
  31. public static final String CLASSNAME_SHADOW = "v-shadow";
  32. /*
  33. * The shadow element for this overlay.
  34. */
  35. private Element shadow;
  36. /**
  37. * The HTML snippet that is used to render the actual shadow. In consists of
  38. * nine different DIV-elements with the following class names:
  39. *
  40. * <pre>
  41. * .v-shadow[-stylename]
  42. * ----------------------------------------------
  43. * | .top-left | .top | .top-right |
  44. * |---------------|-----------|----------------|
  45. * | | | |
  46. * | .left | .center | .right |
  47. * | | | |
  48. * |---------------|-----------|----------------|
  49. * | .bottom-left | .bottom | .bottom-right |
  50. * ----------------------------------------------
  51. * </pre>
  52. *
  53. * See default theme 'shadow.css' for implementation example.
  54. */
  55. private static final String SHADOW_HTML = "<div class=\"top-left\"></div><div class=\"top\"></div><div class=\"top-right\"></div><div class=\"left\"></div><div class=\"center\"></div><div class=\"right\"></div><div class=\"bottom-left\"></div><div class=\"bottom\"></div><div class=\"bottom-right\"></div>";
  56. public VOverlay() {
  57. super();
  58. adjustZIndex();
  59. }
  60. public VOverlay(boolean autoHide) {
  61. super(autoHide);
  62. adjustZIndex();
  63. }
  64. public VOverlay(boolean autoHide, boolean modal) {
  65. super(autoHide, modal);
  66. adjustZIndex();
  67. }
  68. public VOverlay(boolean autoHide, boolean modal, boolean showShadow) {
  69. super(autoHide, modal);
  70. if (showShadow) {
  71. shadow = DOM.createDiv();
  72. shadow.setClassName(CLASSNAME_SHADOW);
  73. shadow.setInnerHTML(SHADOW_HTML);
  74. DOM.setStyleAttribute(shadow, "position", "absolute");
  75. addCloseHandler(new CloseHandler<PopupPanel>() {
  76. public void onClose(CloseEvent<PopupPanel> event) {
  77. if (shadow.getParentElement() != null) {
  78. shadow.getParentElement().removeChild(shadow);
  79. }
  80. }
  81. });
  82. }
  83. adjustZIndex();
  84. }
  85. private void adjustZIndex() {
  86. setZIndex(Z_INDEX);
  87. }
  88. /**
  89. * Set the z-index (visual stack position) for this overlay.
  90. *
  91. * @param zIndex
  92. * The new z-index
  93. */
  94. protected void setZIndex(int zIndex) {
  95. DOM.setStyleAttribute(getElement(), "zIndex", "" + zIndex);
  96. if (shadow != null) {
  97. DOM.setStyleAttribute(shadow, "zIndex", "" + zIndex);
  98. }
  99. }
  100. @Override
  101. public void setPopupPosition(int left, int top) {
  102. super.setPopupPosition(left, top);
  103. if (shadow != null) {
  104. updateShadowSizeAndPosition(isAnimationEnabled() ? 0 : 1);
  105. }
  106. }
  107. @Override
  108. public void show() {
  109. super.show();
  110. if (shadow != null) {
  111. if (isAnimationEnabled()) {
  112. ShadowAnimation sa = new ShadowAnimation();
  113. sa.run(200);
  114. } else {
  115. updateShadowSizeAndPosition(1.0);
  116. }
  117. }
  118. Util.runIE7ZeroSizedBodyFix();
  119. }
  120. @Override
  121. public void hide(boolean autoClosed) {
  122. super.hide(autoClosed);
  123. Util.runIE7ZeroSizedBodyFix();
  124. }
  125. @Override
  126. public void setVisible(boolean visible) {
  127. super.setVisible(visible);
  128. if (shadow != null) {
  129. shadow.getStyle().setProperty("visibility",
  130. visible ? "visible" : "hidden");
  131. }
  132. }
  133. @Override
  134. public void setWidth(String width) {
  135. super.setWidth(width);
  136. if (shadow != null) {
  137. updateShadowSizeAndPosition(1.0);
  138. }
  139. }
  140. @Override
  141. public void setHeight(String height) {
  142. super.setHeight(height);
  143. if (shadow != null) {
  144. updateShadowSizeAndPosition(1.0);
  145. }
  146. }
  147. /**
  148. * Sets the shadow style for this overlay. Will override any previous style
  149. * for the shadow. The default style name is defined by CLASSNAME_SHADOW.
  150. * The given style will be prefixed with CLASSNAME_SHADOW.
  151. *
  152. * @param style
  153. * The new style name for the shadow element. Will be prefixed by
  154. * CLASSNAME_SHADOW, e.g. style=='foobar' -> actual style
  155. * name=='v-shadow-foobar'.
  156. */
  157. protected void setShadowStyle(String style) {
  158. if (shadow != null) {
  159. shadow.setClassName(CLASSNAME_SHADOW + "-" + style);
  160. }
  161. }
  162. /*
  163. * Extending classes should always call this method after they change the
  164. * size of overlay without using normal 'setWidth(String)' and
  165. * 'setHeight(String)' methods (if not calling super.setWidth/Height).
  166. */
  167. protected void updateShadowSizeAndPosition() {
  168. updateShadowSizeAndPosition(1.0);
  169. }
  170. /**
  171. * Recalculates proper position and dimensions for the shadow element. Can
  172. * be used to animate the shadow, using the 'progress' parameter (used to
  173. * animate the shadow in sync with GWT PopupPanel's default animation
  174. * 'PopupPanel.AnimationType.CENTER').
  175. *
  176. * @param progress
  177. * A value between 0.0 and 1.0, indicating the progress of the
  178. * animation (0=start, 1=end).
  179. */
  180. private void updateShadowSizeAndPosition(final double progress) {
  181. // Don't do anything if overlay element is not attached
  182. if (!isAttached()) {
  183. return;
  184. }
  185. // Calculate proper z-index
  186. String zIndex = null;
  187. try {
  188. // Odd behaviour with Windows Hosted Mode forces us to use
  189. // this redundant try/catch block (See dev.vaadin.com #2011)
  190. zIndex = DOM.getStyleAttribute(getElement(), "zIndex");
  191. } catch (Exception ignore) {
  192. // Ignored, will cause no harm
  193. zIndex = "1000";
  194. }
  195. if (zIndex == null) {
  196. zIndex = "" + Z_INDEX;
  197. }
  198. // Calculate position and size
  199. if (BrowserInfo.get().isIE()) {
  200. // Shake IE
  201. getOffsetHeight();
  202. getOffsetWidth();
  203. }
  204. int x = getAbsoluteLeft();
  205. int y = getAbsoluteTop();
  206. /* This is needed for IE7 at least */
  207. // Account for the difference between absolute position and the
  208. // body's positioning context.
  209. x -= Document.get().getBodyOffsetLeft();
  210. y -= Document.get().getBodyOffsetTop();
  211. int width = getOffsetWidth();
  212. int height = getOffsetHeight();
  213. if (width < 0) {
  214. width = 0;
  215. }
  216. if (height < 0) {
  217. height = 0;
  218. }
  219. // Animate the shadow size
  220. x += (int) (width * (1.0 - progress) / 2.0);
  221. y += (int) (height * (1.0 - progress) / 2.0);
  222. width = (int) (width * progress);
  223. height = (int) (height * progress);
  224. // Opera needs some shaking to get parts of the shadow showing
  225. // properly
  226. // (ticket #2704)
  227. if (BrowserInfo.get().isOpera()) {
  228. // Clear the height of all middle elements
  229. DOM.getChild(shadow, 3).getStyle().setProperty("height", "auto");
  230. DOM.getChild(shadow, 4).getStyle().setProperty("height", "auto");
  231. DOM.getChild(shadow, 5).getStyle().setProperty("height", "auto");
  232. }
  233. // Update correct values
  234. DOM.setStyleAttribute(shadow, "zIndex", zIndex);
  235. DOM.setStyleAttribute(shadow, "width", width + "px");
  236. DOM.setStyleAttribute(shadow, "height", height + "px");
  237. DOM.setStyleAttribute(shadow, "top", y + "px");
  238. DOM.setStyleAttribute(shadow, "left", x + "px");
  239. DOM.setStyleAttribute(shadow, "display", progress < 0.9 ? "none" : "");
  240. // Opera fix, part 2 (ticket #2704)
  241. if (BrowserInfo.get().isOpera()) {
  242. // We'll fix the height of all the middle elements
  243. DOM.getChild(shadow, 3)
  244. .getStyle()
  245. .setPropertyPx("height",
  246. DOM.getChild(shadow, 3).getOffsetHeight());
  247. DOM.getChild(shadow, 4)
  248. .getStyle()
  249. .setPropertyPx("height",
  250. DOM.getChild(shadow, 4).getOffsetHeight());
  251. DOM.getChild(shadow, 5)
  252. .getStyle()
  253. .setPropertyPx("height",
  254. DOM.getChild(shadow, 5).getOffsetHeight());
  255. }
  256. // Attach to dom if not there already
  257. if (shadow.getParentElement() == null) {
  258. RootPanel.get().getElement().insertBefore(shadow, getElement());
  259. }
  260. }
  261. protected class ShadowAnimation extends Animation {
  262. @Override
  263. protected void onUpdate(double progress) {
  264. if (shadow != null) {
  265. updateShadowSizeAndPosition(progress);
  266. }
  267. }
  268. }
  269. }