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.

VToolkitOverlay.java 10KB

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