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 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.ui;
  17. import java.util.logging.Level;
  18. import java.util.logging.Logger;
  19. import com.google.gwt.animation.client.Animation;
  20. import com.google.gwt.aria.client.Roles;
  21. import com.google.gwt.core.client.JavaScriptObject;
  22. import com.google.gwt.core.client.Scheduler;
  23. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  24. import com.google.gwt.dom.client.Document;
  25. import com.google.gwt.dom.client.Element;
  26. import com.google.gwt.dom.client.IFrameElement;
  27. import com.google.gwt.dom.client.NativeEvent;
  28. import com.google.gwt.dom.client.Style;
  29. import com.google.gwt.dom.client.Style.BorderStyle;
  30. import com.google.gwt.dom.client.Style.Position;
  31. import com.google.gwt.dom.client.Style.Unit;
  32. import com.google.gwt.event.logical.shared.CloseEvent;
  33. import com.google.gwt.event.logical.shared.CloseHandler;
  34. import com.google.gwt.user.client.DOM;
  35. import com.google.gwt.user.client.Window;
  36. import com.google.gwt.user.client.ui.PopupPanel;
  37. import com.google.gwt.user.client.ui.RootPanel;
  38. import com.google.gwt.user.client.ui.Widget;
  39. import com.vaadin.client.AnimationUtil;
  40. import com.vaadin.client.AnimationUtil.AnimationEndListener;
  41. import com.vaadin.client.ApplicationConnection;
  42. import com.vaadin.client.BrowserInfo;
  43. import com.vaadin.client.ComponentConnector;
  44. import com.vaadin.client.ComputedStyle;
  45. import com.vaadin.client.Util;
  46. import com.vaadin.client.WidgetUtil;
  47. /**
  48. * <p>
  49. * In Vaadin UI this Overlay should always be used for all elements that
  50. * temporary float over other components like context menus etc. This is to deal
  51. * stacking order correctly with VWindow objects.
  52. * </p>
  53. *
  54. * <h3>Shadow</h3>
  55. * <p>
  56. * The separate shadow element underneath the main overlay element is <strong>
  57. * <em>deprecated</em></strong>, and should not be used for new overlay
  58. * components. CSS box-shadow should be used instead of a separate shadow
  59. * element. Remember to include any vendor-prefixed versions to support all
  60. * browsers that you need to. To cover all possible browsers that Vaadin 7
  61. * supports, add <code>-webkit-box-shadow</code> and the standard
  62. * <code>box-shadow</code> properties.
  63. * </p>
  64. *
  65. * <p>
  66. * For IE8, which doesn't support CSS box-shadow, you can use the proprietary
  67. * DropShadow filter. It doesn't provide the exact same features as box-shadow,
  68. * but it is suitable for graceful degradation. Other options are to use a
  69. * border or a pseudo-element underneath the overlay which mimics a shadow, or
  70. * any combination of these.
  71. * </p>
  72. *
  73. * <p>
  74. * Read more about the DropShadow filter from <a
  75. * href="http://msdn.microsoft.com/en-us/library/ms532985(v=vs.85).aspx"
  76. * >Microsoft Developer Network</a>
  77. * </p>
  78. */
  79. public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
  80. @Override
  81. protected void onAttach() {
  82. // Move the overlay to the appropriate overlay container
  83. final VOverlay overlay = VOverlay.current;
  84. if (overlay != null) {
  85. final Element e = overlay.getOverlayContainer();
  86. e.appendChild(getElement());
  87. }
  88. super.onAttach();
  89. }
  90. public static class PositionAndSize {
  91. private int left, top, width, height;
  92. public PositionAndSize(int left, int top, int width, int height) {
  93. super();
  94. setLeft(left);
  95. setTop(top);
  96. setWidth(width);
  97. setHeight(height);
  98. }
  99. public int getLeft() {
  100. return left;
  101. }
  102. public void setLeft(int left) {
  103. this.left = left;
  104. }
  105. public int getTop() {
  106. return top;
  107. }
  108. public void setTop(int top) {
  109. this.top = top;
  110. }
  111. public int getWidth() {
  112. return width;
  113. }
  114. public void setWidth(int width) {
  115. if (width < 0) {
  116. width = 0;
  117. }
  118. this.width = width;
  119. }
  120. public int getHeight() {
  121. return height;
  122. }
  123. public void setHeight(int height) {
  124. if (height < 0) {
  125. height = 0;
  126. }
  127. this.height = height;
  128. }
  129. public void setAnimationFromCenterProgress(double progress) {
  130. left += (int) (width * (1.0 - progress) / 2.0);
  131. top += (int) (height * (1.0 - progress) / 2.0);
  132. width = (int) (width * progress);
  133. height = (int) (height * progress);
  134. }
  135. }
  136. /*
  137. * The z-index value from where all overlays live. This can be overridden in
  138. * any extending class.
  139. */
  140. public static int Z_INDEX = 20000;
  141. private static int leftFix = -1;
  142. private static int topFix = -1;
  143. /**
  144. * Shadow element style. If an extending class wishes to use a different
  145. * style of shadow, it can use setShadowStyle(String) to give the shadow
  146. * element a new style name.
  147. *
  148. * @deprecated See main JavaDoc for VOverlay
  149. */
  150. @Deprecated
  151. public static final String CLASSNAME_SHADOW = "v-shadow";
  152. /**
  153. * Style name for the overlay container element (see
  154. * {@link #getOverlayContainer()}
  155. */
  156. public static final String CLASSNAME_CONTAINER = "v-overlay-container";
  157. /**
  158. * @since 7.3
  159. */
  160. public static final String ADDITIONAL_CLASSNAME_ANIMATE_IN = "animate-in";
  161. /**
  162. * @since 7.3
  163. */
  164. public static final String ADDITIONAL_CLASSNAME_ANIMATE_OUT = "animate-out";
  165. /**
  166. * The shadow element for this overlay.
  167. *
  168. * @deprecated See main JavaDoc for VOverlay
  169. *
  170. */
  171. @Deprecated
  172. private Element shadow;
  173. /*
  174. * The creator of this VOverlay (the widget that made the instance, not the
  175. * layout parent)
  176. */
  177. private Widget owner;
  178. /*
  179. * ApplicationConnection that this overlay belongs to, which is needed to
  180. * create the overlay in the correct container so that the correct styles
  181. * are applied. If not given, owner will be used to figure out, and as a
  182. * last fallback, the overlay is created w/o container, potentially missing
  183. * styles.
  184. */
  185. protected ApplicationConnection ac;
  186. /**
  187. * The shim iframe behind the overlay, allowing PDFs and applets to be
  188. * covered by overlays.
  189. */
  190. private IFrameElement shimElement;
  191. /**
  192. * The HTML snippet that is used to render the actual shadow. In consists of
  193. * nine different DIV-elements with the following class names:
  194. *
  195. * <pre>
  196. * .v-shadow[-stylename]
  197. * ----------------------------------------------
  198. * | .top-left | .top | .top-right |
  199. * |---------------|-----------|----------------|
  200. * | | | |
  201. * | .left | .center | .right |
  202. * | | | |
  203. * |---------------|-----------|----------------|
  204. * | .bottom-left | .bottom | .bottom-right |
  205. * ----------------------------------------------
  206. * </pre>
  207. *
  208. * See default theme 'shadow.css' for implementation example.
  209. *
  210. * @deprecated See main JavaDoc for VOverlay
  211. */
  212. @Deprecated
  213. private static final String SHADOW_HTML = "<div aria-hidden=\"true\" 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>";
  214. /**
  215. * Matches {@link PopupPanel}.ANIMATION_DURATION
  216. */
  217. private static final int POPUP_PANEL_ANIMATION_DURATION = 200;
  218. /**
  219. * @deprecated See main JavaDoc for VOverlay
  220. */
  221. @Deprecated
  222. private boolean sinkShadowEvents = false;
  223. public VOverlay() {
  224. super();
  225. adjustZIndex();
  226. }
  227. public VOverlay(boolean autoHide) {
  228. super(autoHide);
  229. adjustZIndex();
  230. }
  231. public VOverlay(boolean autoHide, boolean modal) {
  232. super(autoHide, modal);
  233. adjustZIndex();
  234. }
  235. /**
  236. * @deprecated See main JavaDoc for VOverlay. Use the other constructors
  237. * without the <code>showShadow</code> parameter.
  238. */
  239. @Deprecated
  240. public VOverlay(boolean autoHide, boolean modal, boolean showShadow) {
  241. super(autoHide, modal);
  242. setShadowEnabled(showShadow && useShadowDiv());
  243. adjustZIndex();
  244. }
  245. /**
  246. * Return true if a separate shadow div should be used. Since Vaadin 7.3,
  247. * shadows are implemented with CSS box-shadow. Thus, a shadow div is only
  248. * used for IE8 by default.
  249. *
  250. * @deprecated See main JavaDoc for VOverlay
  251. * @since 7.3
  252. * @return true to use a shadow div
  253. */
  254. @Deprecated
  255. protected boolean useShadowDiv() {
  256. return BrowserInfo.get().isIE8();
  257. }
  258. /**
  259. * Method to control whether DOM elements for shadow are added. With this
  260. * method subclasses can control displaying of shadow also after the
  261. * constructor.
  262. *
  263. * @param enabled
  264. * true if shadow should be displayed
  265. *
  266. * @deprecated See main JavaDoc for VOverlay
  267. */
  268. @Deprecated
  269. protected void setShadowEnabled(boolean enabled) {
  270. if (enabled != isShadowEnabled()) {
  271. if (enabled) {
  272. shadow = DOM.createDiv();
  273. shadow.setClassName(CLASSNAME_SHADOW);
  274. shadow.setInnerHTML(SHADOW_HTML);
  275. shadow.getStyle().setPosition(Position.ABSOLUTE);
  276. addCloseHandler(this);
  277. } else {
  278. removeShadowIfPresent();
  279. shadow = null;
  280. }
  281. }
  282. }
  283. /**
  284. * @deprecated See main JavaDoc for VOverlay
  285. */
  286. @Deprecated
  287. protected boolean isShadowEnabled() {
  288. return shadow != null;
  289. }
  290. protected boolean isShimElementEnabled() {
  291. return shimElement != null;
  292. }
  293. private void removeShimElement() {
  294. if (shimElement != null) {
  295. shimElement.removeFromParent();
  296. }
  297. }
  298. /**
  299. * @deprecated See main JavaDoc for VOverlay
  300. */
  301. @Deprecated
  302. private void removeShadowIfPresent() {
  303. if (isShadowAttached()) {
  304. // Remove event listener from the shadow
  305. unsinkShadowEvents();
  306. shadow.removeFromParent();
  307. }
  308. }
  309. /**
  310. * @deprecated See main JavaDoc for VOverlay
  311. */
  312. @Deprecated
  313. private boolean isShadowAttached() {
  314. return isShadowEnabled() && shadow.getParentElement() != null;
  315. }
  316. private void adjustZIndex() {
  317. setZIndex(Z_INDEX);
  318. }
  319. /**
  320. * Set the z-index (visual stack position) for this overlay.
  321. *
  322. * @param zIndex
  323. * The new z-index
  324. */
  325. protected void setZIndex(int zIndex) {
  326. getElement().getStyle().setZIndex(zIndex);
  327. if (isShadowEnabled()) {
  328. shadow.getStyle().setZIndex(zIndex);
  329. }
  330. }
  331. @Override
  332. public void setPopupPosition(int left, int top) {
  333. // TODO, this should in fact be part of
  334. // Document.get().getBodyOffsetLeft/Top(). Would require overriding DOM
  335. // for all permutations. Now adding fix as margin instead of fixing
  336. // left/top because parent class saves the position.
  337. Style style = getElement().getStyle();
  338. style.setMarginLeft(-adjustByRelativeLeftBodyMargin(), Unit.PX);
  339. style.setMarginTop(-adjustByRelativeTopBodyMargin(), Unit.PX);
  340. super.setPopupPosition(left, top);
  341. positionOrSizeUpdated(isAnimationEnabled() ? 0 : 1);
  342. }
  343. private IFrameElement getShimElement() {
  344. if (shimElement == null && needsShimElement()) {
  345. shimElement = Document.get().createIFrameElement();
  346. // Insert shim iframe before the main overlay element. It does not
  347. // matter if it is in front or behind the shadow as we cannot put a
  348. // shim behind the shadow due to its transparency.
  349. shimElement.getStyle().setPosition(Position.ABSOLUTE);
  350. shimElement.getStyle().setBorderStyle(BorderStyle.NONE);
  351. shimElement.setTabIndex(-1);
  352. shimElement.setFrameBorder(0);
  353. shimElement.setMarginHeight(0);
  354. }
  355. return shimElement;
  356. }
  357. private int getActualTop() {
  358. int y = getAbsoluteTop();
  359. /* This is needed for IE7 at least */
  360. // Account for the difference between absolute position and the
  361. // body's positioning context.
  362. y -= Document.get().getBodyOffsetTop();
  363. y -= adjustByRelativeTopBodyMargin();
  364. return y;
  365. }
  366. private int getActualLeft() {
  367. int x = getAbsoluteLeft();
  368. /* This is needed for IE7 at least */
  369. // Account for the difference between absolute position and the
  370. // body's positioning context.
  371. x -= Document.get().getBodyOffsetLeft();
  372. x -= adjustByRelativeLeftBodyMargin();
  373. return x;
  374. }
  375. private static int adjustByRelativeTopBodyMargin() {
  376. if (topFix == -1) {
  377. topFix = detectRelativeBodyFixes("top");
  378. }
  379. return topFix;
  380. }
  381. private native static int detectRelativeBodyFixes(String axis)
  382. /*-{
  383. try {
  384. var b = $wnd.document.body;
  385. var cstyle = b.currentStyle ? b.currentStyle : getComputedStyle(b);
  386. if(cstyle && cstyle.position == 'relative') {
  387. return b.getBoundingClientRect()[axis];
  388. }
  389. } catch(e){}
  390. return 0;
  391. }-*/;
  392. private static int adjustByRelativeLeftBodyMargin() {
  393. if (leftFix == -1) {
  394. leftFix = detectRelativeBodyFixes("left");
  395. }
  396. return leftFix;
  397. }
  398. /*
  399. * A "thread local" of sorts, set temporarily so that VOverlayImpl knows
  400. * which VOverlay is using it, so that it can be attached to the correct
  401. * overlay container.
  402. *
  403. * TODO this is a strange pattern that we should get rid of when possible.
  404. */
  405. protected static VOverlay current;
  406. @Override
  407. public void show() {
  408. current = this;
  409. maybeShowWithAnimation();
  410. if (isAnimationEnabled()) {
  411. new ResizeAnimation().run(POPUP_PANEL_ANIMATION_DURATION);
  412. } else {
  413. if (BrowserInfo.get().isIE8()) {
  414. Scheduler.get().scheduleFinally(new ScheduledCommand() {
  415. @Override
  416. public void execute() {
  417. positionOrSizeUpdated(1.0);
  418. }
  419. });
  420. } else {
  421. positionOrSizeUpdated(1.0);
  422. }
  423. }
  424. current = null;
  425. }
  426. private JavaScriptObject animateInListener;
  427. private boolean maybeShowWithAnimation() {
  428. boolean isAttached = isAttached() && isShowing();
  429. super.show();
  430. // Don't animate if already visible or browser is IE8 or IE9 (no CSS
  431. // animation support)
  432. if (isAttached || BrowserInfo.get().isIE8()
  433. || BrowserInfo.get().isIE9()) {
  434. return false;
  435. } else {
  436. // Check if animations are used
  437. setVisible(false);
  438. addStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
  439. if (isShadowEnabled()) {
  440. shadow.addClassName(CLASSNAME_SHADOW + "-"
  441. + ADDITIONAL_CLASSNAME_ANIMATE_IN);
  442. }
  443. ComputedStyle cs = new ComputedStyle(getElement());
  444. String animationName = AnimationUtil.getAnimationName(cs);
  445. if (animationName == null) {
  446. animationName = "";
  447. }
  448. setVisible(true);
  449. if (animationName.contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  450. // Disable GWT PopupPanel animation if used
  451. setAnimationEnabled(false);
  452. animateInListener = AnimationUtil.addAnimationEndListener(
  453. getElement(), new AnimationEndListener() {
  454. @Override
  455. public void onAnimationEnd(NativeEvent event) {
  456. String animationName = AnimationUtil
  457. .getAnimationName(event);
  458. if (animationName
  459. .contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  460. AnimationUtil.removeAnimationEndListener(
  461. getElement(), animateInListener);
  462. removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
  463. if (isShadowEnabled()) {
  464. shadow.removeClassName(CLASSNAME_SHADOW
  465. + "-"
  466. + ADDITIONAL_CLASSNAME_ANIMATE_IN);
  467. }
  468. }
  469. }
  470. });
  471. return true;
  472. } else {
  473. removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
  474. if (isShadowEnabled()) {
  475. shadow.removeClassName(CLASSNAME_SHADOW + "-"
  476. + ADDITIONAL_CLASSNAME_ANIMATE_IN);
  477. }
  478. return false;
  479. }
  480. }
  481. }
  482. @Override
  483. protected void onDetach() {
  484. super.onDetach();
  485. // Always ensure shadow is removed when the overlay is removed.
  486. removeShadowIfPresent();
  487. removeShimElement();
  488. }
  489. @Override
  490. public void setVisible(boolean visible) {
  491. super.setVisible(visible);
  492. if (isShadowEnabled()) {
  493. shadow.getStyle().setProperty("visibility",
  494. visible ? "visible" : "hidden");
  495. }
  496. if (isShimElementEnabled()) {
  497. shimElement.getStyle().setProperty("visibility",
  498. visible ? "visible" : "hidden");
  499. }
  500. }
  501. @Override
  502. public void setWidth(String width) {
  503. super.setWidth(width);
  504. positionOrSizeUpdated(1.0);
  505. }
  506. @Override
  507. public void setHeight(String height) {
  508. super.setHeight(height);
  509. positionOrSizeUpdated(1.0);
  510. }
  511. /**
  512. * Sets the shadow style for this overlay. Will override any previous style
  513. * for the shadow. The default style name is defined by CLASSNAME_SHADOW.
  514. * The given style will be prefixed with CLASSNAME_SHADOW.
  515. *
  516. * @param style
  517. * The new style name for the shadow element. Will be prefixed by
  518. * CLASSNAME_SHADOW, e.g. style=='foobar' -> actual style
  519. * name=='v-shadow-foobar'.
  520. *
  521. * @deprecated See main JavaDoc for VOverlay
  522. */
  523. @Deprecated
  524. protected void setShadowStyle(String style) {
  525. if (isShadowEnabled()) {
  526. shadow.setClassName(CLASSNAME_SHADOW + "-" + style);
  527. }
  528. }
  529. /**
  530. * Extending classes should always call this method after they change the
  531. * size of overlay without using normal 'setWidth(String)' and
  532. * 'setHeight(String)' methods (if not calling super.setWidth/Height).
  533. *
  534. */
  535. public void positionOrSizeUpdated() {
  536. positionOrSizeUpdated(1.0);
  537. }
  538. /**
  539. * @deprecated Call {@link #positionOrSizeUpdated()} instead.
  540. */
  541. @Deprecated
  542. protected void updateShadowSizeAndPosition() {
  543. positionOrSizeUpdated();
  544. }
  545. /**
  546. * Recalculates proper position and dimensions for the shadow and shim
  547. * elements. Can be used to animate the related elements, using the
  548. * 'progress' parameter (used to animate the shadow in sync with GWT
  549. * PopupPanel's default animation 'PopupPanel.AnimationType.CENTER').
  550. *
  551. * @param progress
  552. * A value between 0.0 and 1.0, indicating the progress of the
  553. * animation (0=start, 1=end).
  554. */
  555. private void positionOrSizeUpdated(final double progress) {
  556. // Don't do anything if overlay element is not attached
  557. if (!isAttached()) {
  558. return;
  559. }
  560. // Calculate proper z-index
  561. int zIndex = -1;
  562. try {
  563. // Odd behaviour with Windows Hosted Mode forces us to use
  564. // this redundant try/catch block (See dev.vaadin.com #2011)
  565. zIndex = Integer.parseInt(getElement().getStyle().getZIndex());
  566. } catch (Exception ignore) {
  567. // Ignored, will cause no harm
  568. zIndex = 1000;
  569. }
  570. if (zIndex == -1) {
  571. zIndex = Z_INDEX;
  572. }
  573. // Calculate position and size
  574. if (BrowserInfo.get().isIE()) {
  575. // Shake IE
  576. getOffsetHeight();
  577. getOffsetWidth();
  578. }
  579. if (isShadowEnabled() || needsShimElement()) {
  580. PositionAndSize positionAndSize = new PositionAndSize(
  581. getActualLeft(), getActualTop(), getOffsetWidth(),
  582. getOffsetHeight());
  583. // Animate the size
  584. positionAndSize.setAnimationFromCenterProgress(progress);
  585. Element container = getElement().getParentElement();
  586. if (isShadowEnabled()) {
  587. updateShadowPosition(progress, zIndex, positionAndSize);
  588. if (shadow.getParentElement() == null) {
  589. container.insertBefore(shadow, getElement());
  590. sinkShadowEvents();
  591. }
  592. }
  593. if (needsShimElement()) {
  594. updateShimPosition(positionAndSize);
  595. if (shimElement.getParentElement() == null) {
  596. container.insertBefore(shimElement, getElement());
  597. }
  598. }
  599. }
  600. // Fix for #14173
  601. // IE9 and IE10 have a bug, when resize an a element with box-shadow.
  602. // IE9 and IE10 need explicit update to remove extra box-shadows
  603. if (BrowserInfo.get().isIE9() || BrowserInfo.get().isIE10()) {
  604. WidgetUtil.forceIERedraw(getElement());
  605. }
  606. }
  607. /**
  608. * @deprecated See main JavaDoc for VOverlay
  609. */
  610. @Deprecated
  611. private void updateShadowPosition(final double progress, int zIndex,
  612. PositionAndSize positionAndSize) {
  613. // Opera needs some shaking to get parts of the shadow showing
  614. // properly (ticket #2704)
  615. if (BrowserInfo.get().isOpera()) {
  616. // Clear the height of all middle elements
  617. DOM.getChild(shadow, 3).getStyle().setProperty("height", "auto");
  618. DOM.getChild(shadow, 4).getStyle().setProperty("height", "auto");
  619. DOM.getChild(shadow, 5).getStyle().setProperty("height", "auto");
  620. }
  621. updatePositionAndSize(shadow, positionAndSize);
  622. shadow.getStyle().setZIndex(zIndex);
  623. shadow.getStyle().setProperty("display", progress < 0.9 ? "none" : "");
  624. // Opera fix, part 2 (ticket #2704)
  625. if (BrowserInfo.get().isOpera()) {
  626. // We'll fix the height of all the middle elements
  627. DOM.getChild(shadow, 3)
  628. .getStyle()
  629. .setPropertyPx("height",
  630. DOM.getChild(shadow, 3).getOffsetHeight());
  631. DOM.getChild(shadow, 4)
  632. .getStyle()
  633. .setPropertyPx("height",
  634. DOM.getChild(shadow, 4).getOffsetHeight());
  635. DOM.getChild(shadow, 5)
  636. .getStyle()
  637. .setPropertyPx("height",
  638. DOM.getChild(shadow, 5).getOffsetHeight());
  639. }
  640. }
  641. private void updateShimPosition(PositionAndSize positionAndSize) {
  642. updatePositionAndSize(getShimElement(), positionAndSize);
  643. }
  644. /**
  645. * Returns true if we should add a shim iframe below the overlay to deal
  646. * with zindex issues with PDFs and applets. Can be overriden to disable
  647. * shim iframes if they are not needed.
  648. *
  649. * @return true if a shim iframe should be added, false otherwise
  650. */
  651. protected boolean needsShimElement() {
  652. BrowserInfo info = BrowserInfo.get();
  653. return info.isIE() && info.isBrowserVersionNewerOrEqual(8, 0);
  654. }
  655. private void updatePositionAndSize(Element e,
  656. PositionAndSize positionAndSize) {
  657. e.getStyle().setLeft(positionAndSize.getLeft(), Unit.PX);
  658. e.getStyle().setTop(positionAndSize.getTop(), Unit.PX);
  659. e.getStyle().setWidth(positionAndSize.getWidth(), Unit.PX);
  660. e.getStyle().setHeight(positionAndSize.getHeight(), Unit.PX);
  661. }
  662. protected class ResizeAnimation extends Animation {
  663. @Override
  664. protected void onUpdate(double progress) {
  665. positionOrSizeUpdated(progress);
  666. }
  667. }
  668. @Override
  669. public void onClose(CloseEvent<PopupPanel> event) {
  670. removeShadowIfPresent();
  671. }
  672. @Override
  673. public void sinkEvents(int eventBitsToAdd) {
  674. super.sinkEvents(eventBitsToAdd);
  675. // Also sink events on the shadow if present
  676. sinkShadowEvents();
  677. }
  678. /**
  679. * @deprecated See main JavaDoc for VOverlay
  680. */
  681. @Deprecated
  682. private void sinkShadowEvents() {
  683. if (isSinkShadowEvents() && isShadowAttached()) {
  684. // Sink the same events as the actual overlay has sunk
  685. DOM.sinkEvents(shadow, DOM.getEventsSunk(getElement()));
  686. // Send events to VOverlay.onBrowserEvent
  687. DOM.setEventListener(shadow, this);
  688. }
  689. }
  690. /**
  691. * @deprecated See main JavaDoc for VOverlay
  692. */
  693. @Deprecated
  694. private void unsinkShadowEvents() {
  695. if (isShadowAttached()) {
  696. DOM.setEventListener(shadow, null);
  697. DOM.sinkEvents(shadow, 0);
  698. }
  699. }
  700. /**
  701. * Enables or disables sinking the events of the shadow to the same
  702. * onBrowserEvent as events to the actual overlay goes.
  703. *
  704. * Please note, that if you enable this, you can't assume that e.g.
  705. * event.getEventTarget returns an element inside the DOM structure of the
  706. * overlay
  707. *
  708. * @param sinkShadowEvents
  709. *
  710. * @deprecated See main JavaDoc for VOverlay
  711. */
  712. @Deprecated
  713. protected void setSinkShadowEvents(boolean sinkShadowEvents) {
  714. this.sinkShadowEvents = sinkShadowEvents;
  715. if (sinkShadowEvents) {
  716. sinkShadowEvents();
  717. } else {
  718. unsinkShadowEvents();
  719. }
  720. }
  721. /**
  722. * @deprecated See main JavaDoc for VOverlay
  723. */
  724. @Deprecated
  725. protected boolean isSinkShadowEvents() {
  726. return sinkShadowEvents;
  727. }
  728. /**
  729. * Get owner (Widget that made this VOverlay, not the layout parent) of
  730. * VOverlay
  731. *
  732. * @return Owner (creator) or null if not defined
  733. */
  734. public Widget getOwner() {
  735. return owner;
  736. }
  737. /**
  738. * Set owner (Widget that made this VOverlay, not the layout parent) of
  739. * VOverlay
  740. *
  741. * @param owner
  742. * Owner (creator) of VOverlay
  743. */
  744. public void setOwner(Widget owner) {
  745. this.owner = owner;
  746. }
  747. /**
  748. * Get the {@link ApplicationConnection} that this overlay belongs to. If
  749. * it's not set, {@link #getOwner()} is used to figure it out.
  750. *
  751. * @return
  752. */
  753. protected ApplicationConnection getApplicationConnection() {
  754. if (ac != null) {
  755. return ac;
  756. } else if (owner != null) {
  757. ComponentConnector c = Util.findConnectorFor(owner);
  758. if (c != null) {
  759. ac = c.getConnection();
  760. }
  761. return ac;
  762. } else {
  763. return null;
  764. }
  765. }
  766. /**
  767. * Gets the 'overlay container' element. Tries to find the current
  768. * {@link ApplicationConnection} using {@link #getApplicationConnection()}.
  769. *
  770. * @return the overlay container element for the current
  771. * {@link ApplicationConnection} or another element if the current
  772. * {@link ApplicationConnection} cannot be determined.
  773. */
  774. public com.google.gwt.user.client.Element getOverlayContainer() {
  775. ApplicationConnection ac = getApplicationConnection();
  776. if (ac == null) {
  777. // could not figure out which one we belong to, styling will
  778. // probably fail
  779. Logger.getLogger(getClass().getSimpleName())
  780. .log(Level.WARNING,
  781. "Could not determine ApplicationConnection for Overlay. Overlay will be attached directly to the root panel");
  782. return RootPanel.get().getElement();
  783. } else {
  784. return getOverlayContainer(ac);
  785. }
  786. }
  787. /**
  788. * Gets the 'overlay container' element pertaining to the given
  789. * {@link ApplicationConnection}. Each overlay should be created in a
  790. * overlay container element, so that the correct theme and styles can be
  791. * applied.
  792. *
  793. * @param ac
  794. * A reference to {@link ApplicationConnection}
  795. * @return The overlay container
  796. */
  797. public static com.google.gwt.user.client.Element getOverlayContainer(
  798. ApplicationConnection ac) {
  799. String id = ac.getConfiguration().getRootPanelId();
  800. id = id += "-overlays";
  801. Element container = DOM.getElementById(id);
  802. if (container == null) {
  803. container = DOM.createDiv();
  804. container.setId(id);
  805. String styles = ac.getUIConnector().getWidget().getParent()
  806. .getStyleName();
  807. if (styles != null && !styles.equals("")) {
  808. container.addClassName(styles);
  809. }
  810. container.addClassName(CLASSNAME_CONTAINER);
  811. RootPanel.get().getElement().appendChild(container);
  812. }
  813. return DOM.asOld(container);
  814. }
  815. /**
  816. * Set the label of the container element, where tooltip, notification and
  817. * dialgs are added to.
  818. *
  819. * @param applicationConnection
  820. * the application connection for which to change the label
  821. * @param overlayContainerLabel
  822. * label for the container
  823. */
  824. public static void setOverlayContainerLabel(
  825. ApplicationConnection applicationConnection,
  826. String overlayContainerLabel) {
  827. Roles.getAlertRole().setAriaLabelProperty(
  828. VOverlay.getOverlayContainer(applicationConnection),
  829. overlayContainerLabel);
  830. }
  831. @Override
  832. public void center() {
  833. super.center();
  834. // Some devices can be zoomed in, we should center to the visual
  835. // viewport for those devices
  836. BrowserInfo b = BrowserInfo.get();
  837. if (b.isAndroid() || b.isIOS()) {
  838. int left = (getVisualViewportWidth() - getOffsetWidth()) >> 1;
  839. int top = (getVisualViewportHeight() - getOffsetHeight()) >> 1;
  840. setPopupPosition(Math.max(Window.getScrollLeft() + left, 0),
  841. Math.max(Window.getScrollTop() + top, 0));
  842. }
  843. }
  844. /**
  845. * Gets the visual viewport width, which is useful for e.g iOS where the
  846. * view can be zoomed in while keeping the layout viewport intact.
  847. *
  848. * Falls back to layout viewport; for those browsers/devices the difference
  849. * is that the scrollbar with is included (if there is a scrollbar).
  850. *
  851. * @since 7.0.7
  852. * @return
  853. */
  854. private int getVisualViewportWidth() {
  855. int w = (int) getSubpixelInnerWidth();
  856. if (w < 0) {
  857. return Window.getClientWidth();
  858. } else {
  859. return w;
  860. }
  861. }
  862. /**
  863. * Gets the visual viewport height, which is useful for e.g iOS where the
  864. * view can be zoomed in while keeping the layout viewport intact.
  865. *
  866. * Falls back to layout viewport; for those browsers/devices the difference
  867. * is that the scrollbar with is included (if there is a scrollbar).
  868. *
  869. * @since 7.0.7
  870. * @return
  871. */
  872. private int getVisualViewportHeight() {
  873. int h = (int) getSubpixelInnerHeight();
  874. if (h < 0) {
  875. return Window.getClientHeight();
  876. } else {
  877. return h;
  878. }
  879. }
  880. private native double getSubpixelInnerWidth()
  881. /*-{
  882. return $wnd.innerWidth !== undefined ? $wnd.innerWidth : -1;
  883. }-*/;
  884. private native double getSubpixelInnerHeight()
  885. /*-{
  886. return $wnd.innerHeight !== undefined ? $wnd.innerHeight :-1;
  887. }-*/;
  888. /*
  889. * (non-Javadoc)
  890. *
  891. * @see com.google.gwt.user.client.ui.PopupPanel#hide()
  892. */
  893. @Override
  894. public void hide() {
  895. hide(false);
  896. }
  897. /*
  898. * (non-Javadoc)
  899. *
  900. * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean)
  901. */
  902. @Override
  903. public void hide(final boolean autoClosed) {
  904. hide(autoClosed, true, true);
  905. }
  906. /**
  907. *
  908. * Hides the popup and detaches it from the page. This has no effect if it
  909. * is not currently showing. Animation-in, animation-out can be
  910. * enable/disabled for different use cases.
  911. *
  912. * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean)
  913. *
  914. * @param autoClosed
  915. * the value that will be passed to
  916. * {@link CloseHandler#onClose(CloseEvent)} when the popup is
  917. * closed
  918. * @param animateIn
  919. * enable/disable animate-in animation
  920. * @param animateOut
  921. * enable/disable animate-out animation
  922. * @since 7.3.7
  923. */
  924. public void hide(final boolean autoClosed, final boolean animateIn,
  925. final boolean animateOut) {
  926. if (BrowserInfo.get().isIE8() || BrowserInfo.get().isIE9()) {
  927. super.hide(autoClosed);
  928. } else {
  929. if (animateIn
  930. && getStyleName().contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  931. AnimationUtil.addAnimationEndListener(getElement(),
  932. new AnimationEndListener() {
  933. @Override
  934. public void onAnimationEnd(NativeEvent event) {
  935. if (AnimationUtil
  936. .getAnimationName(event)
  937. .contains(
  938. ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  939. VOverlay.this.hide(autoClosed);
  940. }
  941. }
  942. });
  943. } else {
  944. // Check if animations are used
  945. addStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  946. if (isShadowEnabled()) {
  947. shadow.addClassName(CLASSNAME_SHADOW + "-"
  948. + ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  949. }
  950. ComputedStyle cs = new ComputedStyle(getElement());
  951. String animationName = AnimationUtil.getAnimationName(cs);
  952. if (animationName == null) {
  953. animationName = "";
  954. }
  955. if (animateOut
  956. && animationName
  957. .contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
  958. // Disable GWT PopupPanel closing animation if used
  959. setAnimationEnabled(false);
  960. AnimationUtil.addAnimationEndListener(getElement(),
  961. new AnimationEndListener() {
  962. @Override
  963. public void onAnimationEnd(NativeEvent event) {
  964. String animationName = AnimationUtil
  965. .getAnimationName(event);
  966. if (animationName
  967. .contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
  968. AnimationUtil
  969. .removeAllAnimationEndListeners(getElement());
  970. // Remove both animation styles just in
  971. // case
  972. removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
  973. removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  974. if (isShadowEnabled()) {
  975. shadow.removeClassName(CLASSNAME_SHADOW
  976. + "-"
  977. + ADDITIONAL_CLASSNAME_ANIMATE_IN);
  978. shadow.removeClassName(CLASSNAME_SHADOW
  979. + "-"
  980. + ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  981. }
  982. VOverlay.super.hide(autoClosed);
  983. }
  984. }
  985. });
  986. // No event previews should happen after the animation has
  987. // started
  988. VOverlay.this.setPreviewingAllNativeEvents(false);
  989. } else {
  990. removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  991. if (isShadowEnabled()) {
  992. shadow.removeClassName(CLASSNAME_SHADOW + "-"
  993. + ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  994. }
  995. super.hide(autoClosed);
  996. }
  997. }
  998. }
  999. }
  1000. }