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.

Overlay.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. /*
  2. * Copyright 2000-2016 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.widgets;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. import com.google.gwt.animation.client.Animation;
  20. import com.google.gwt.core.client.GWT;
  21. import com.google.gwt.dom.client.Document;
  22. import com.google.gwt.dom.client.Element;
  23. import com.google.gwt.dom.client.IFrameElement;
  24. import com.google.gwt.dom.client.NativeEvent;
  25. import com.google.gwt.dom.client.Style;
  26. import com.google.gwt.dom.client.Style.BorderStyle;
  27. import com.google.gwt.dom.client.Style.Position;
  28. import com.google.gwt.dom.client.Style.Unit;
  29. import com.google.gwt.event.logical.shared.CloseEvent;
  30. import com.google.gwt.event.logical.shared.CloseHandler;
  31. import com.google.gwt.user.client.Command;
  32. import com.google.gwt.user.client.Window;
  33. import com.google.gwt.user.client.ui.PopupPanel;
  34. import com.google.gwt.user.client.ui.RootPanel;
  35. import com.google.gwt.user.client.ui.Widget;
  36. import com.vaadin.client.AnimationUtil;
  37. import com.vaadin.client.AnimationUtil.AnimationEndListener;
  38. import com.vaadin.client.BrowserInfo;
  39. import com.vaadin.client.ComputedStyle;
  40. /**
  41. * Overlay widget extending the PopupPanel. Overlay is used to float elements on
  42. * top of other elements temporarily.
  43. * <p>
  44. * <b>Note:</b> This class should always be constructed with
  45. * {@link GWT#create(Class)}.
  46. *
  47. * @since 7.6.1
  48. */
  49. public class Overlay extends PopupPanel {
  50. @Override
  51. protected void onAttach() {
  52. // Move the overlay to the appropriate overlay container
  53. final Overlay overlay = Overlay.current;
  54. if (overlay != null) {
  55. final Element e = overlay.getOverlayContainer();
  56. e.appendChild(getElement());
  57. }
  58. super.onAttach();
  59. }
  60. public static class PositionAndSize {
  61. private int left, top, width, height;
  62. public PositionAndSize(int left, int top, int width, int height) {
  63. super();
  64. setLeft(left);
  65. setTop(top);
  66. setWidth(width);
  67. setHeight(height);
  68. }
  69. public int getLeft() {
  70. return left;
  71. }
  72. public void setLeft(int left) {
  73. this.left = left;
  74. }
  75. public int getTop() {
  76. return top;
  77. }
  78. public void setTop(int top) {
  79. this.top = top;
  80. }
  81. public int getWidth() {
  82. return width;
  83. }
  84. public void setWidth(int width) {
  85. if (width < 0) {
  86. width = 0;
  87. }
  88. this.width = width;
  89. }
  90. public int getHeight() {
  91. return height;
  92. }
  93. public void setHeight(int height) {
  94. if (height < 0) {
  95. height = 0;
  96. }
  97. this.height = height;
  98. }
  99. public void setAnimationFromCenterProgress(double progress) {
  100. left += (int) (width * (1.0 - progress) / 2.0);
  101. top += (int) (height * (1.0 - progress) / 2.0);
  102. width = (int) (width * progress);
  103. height = (int) (height * progress);
  104. }
  105. }
  106. /*
  107. * The z-index value from where all overlays live. This can be overridden in
  108. * any extending class.
  109. */
  110. public static int Z_INDEX = 20000;
  111. private static int leftFix = -1;
  112. private static int topFix = -1;
  113. /**
  114. * Shadow element style. If an extending class wishes to use a different
  115. * style of shadow, it can use setShadowStyle(String) to give the shadow
  116. * element a new style name.
  117. *
  118. * @deprecated See main JavaDoc for Overlay
  119. */
  120. @Deprecated
  121. public static final String CLASSNAME_SHADOW = "v-shadow";
  122. /**
  123. * Style name for the overlay container element (see
  124. * {@link #getOverlayContainer()}
  125. */
  126. public static final String CLASSNAME_CONTAINER = "v-overlay-container";
  127. /**
  128. * @since 7.3
  129. */
  130. public static final String ADDITIONAL_CLASSNAME_ANIMATE_IN = "animate-in";
  131. /**
  132. * @since 7.3
  133. */
  134. public static final String ADDITIONAL_CLASSNAME_ANIMATE_OUT = "animate-out";
  135. /*
  136. * The creator of this Overlay (the widget that made the instance, not the
  137. * layout parent)
  138. */
  139. private Widget owner;
  140. /**
  141. * The shim iframe behind the overlay, allowing PDFs and applets to be
  142. * covered by overlays.
  143. */
  144. private IFrameElement shimElement;
  145. /**
  146. * Matches {@link PopupPanel}.ANIMATION_DURATION
  147. */
  148. private static final int POPUP_PANEL_ANIMATION_DURATION = 200;
  149. private List<Command> runOnClose = new ArrayList<>();
  150. public Overlay() {
  151. super();
  152. adjustZIndex();
  153. }
  154. public Overlay(boolean autoHide) {
  155. super(autoHide);
  156. adjustZIndex();
  157. }
  158. public Overlay(boolean autoHide, boolean modal) {
  159. super(autoHide, modal);
  160. adjustZIndex();
  161. }
  162. protected boolean isShimElementEnabled() {
  163. return shimElement != null;
  164. }
  165. private void removeShimElement() {
  166. if (shimElement != null) {
  167. shimElement.removeFromParent();
  168. }
  169. }
  170. private void adjustZIndex() {
  171. setZIndex(Z_INDEX);
  172. }
  173. /**
  174. * Set the z-index (visual stack position) for this overlay.
  175. *
  176. * @param zIndex
  177. * The new z-index
  178. */
  179. protected void setZIndex(int zIndex) {
  180. getElement().getStyle().setZIndex(zIndex);
  181. }
  182. @Override
  183. public void setPopupPosition(int left, int top) {
  184. // PopupPanel tries to position the popup on screen (by
  185. // default right, below) and will move it if there is not
  186. // enough space right or below but only if there is
  187. // sufficient space left or above. If the popup is too big
  188. // to fit on either side, it will be in the original
  189. // position.
  190. if (isFitInWindow()) {
  191. int windowLeft = Window.getScrollLeft();
  192. int windowRight = Window.getScrollLeft() + Window.getClientWidth();
  193. int width = getOffsetWidth();
  194. int popupRight = left + width;
  195. int popupRightOfWindow = popupRight - windowRight;
  196. if (popupRightOfWindow > 0) {
  197. // Popup is too large to fit
  198. left -= popupRightOfWindow;
  199. if (left < 0) {
  200. // Would move left of screen, shrink to fit in window
  201. setOuterWidthThroughWidget(windowRight - windowLeft);
  202. runOnClose.add(new Command() {
  203. @Override
  204. public void execute() {
  205. getWidget().setWidth("");
  206. }
  207. });
  208. left = 0;
  209. }
  210. }
  211. int windowTop = Window.getScrollTop();
  212. int windowBottom = Window.getScrollTop() + Window.getClientHeight();
  213. int height = getOffsetHeight();
  214. int popupBottom = top + height;
  215. int popupBelowWindow = popupBottom - windowBottom;
  216. if (popupBelowWindow > 0) {
  217. // Popup is too large to fit
  218. top -= popupBelowWindow;
  219. if (top < 0) {
  220. // Would move above screen, shrink to fit in window
  221. setOuterHeightThroughWidget(windowBottom - windowTop);
  222. runOnClose.add(new Command() {
  223. @Override
  224. public void execute() {
  225. getWidget().setHeight("");
  226. }
  227. });
  228. top = 0;
  229. }
  230. }
  231. }
  232. // TODO, this should in fact be part of
  233. // Document.get().getBodyOffsetLeft/Top(). Would require overriding DOM
  234. // for all permutations. Now adding fix as margin instead of fixing
  235. // left/top because parent class saves the position.
  236. Style style = getElement().getStyle();
  237. style.setMarginLeft(-adjustByRelativeLeftBodyMargin(), Unit.PX);
  238. style.setMarginTop(-adjustByRelativeTopBodyMargin(), Unit.PX);
  239. super.setPopupPosition(left, top);
  240. positionOrSizeUpdated(isAnimationEnabled() ? 0 : 1);
  241. }
  242. private void setOuterHeightThroughWidget(int outerHeight) {
  243. getWidget().setHeight(outerHeight + "px");
  244. // Take margin/border/padding into account if needed
  245. // (the height is for the overlay root but we set it on the
  246. // widget)
  247. int adjustedHeight = outerHeight - (getOffsetHeight() - outerHeight);
  248. if (adjustedHeight != outerHeight) {
  249. getWidget().setHeight(adjustedHeight + "px");
  250. }
  251. }
  252. private void setOuterWidthThroughWidget(int outerWidth) {
  253. getWidget().setWidth(outerWidth + "px");
  254. // Take margin/border/padding into account if needed
  255. // (the height is for the overlay root but we set it on the
  256. // widget)
  257. int adjustedWidth = outerWidth - (getOffsetWidth() - outerWidth);
  258. if (adjustedWidth != outerWidth) {
  259. getWidget().setWidth(adjustedWidth + "px");
  260. }
  261. }
  262. private IFrameElement getShimElement() {
  263. if (shimElement == null && needsShimElement()) {
  264. shimElement = Document.get().createIFrameElement();
  265. // Insert shim iframe before the main overlay element. It does not
  266. // matter if it is in front or behind the shadow as we cannot put a
  267. // shim behind the shadow due to its transparency.
  268. shimElement.getStyle().setPosition(Position.ABSOLUTE);
  269. shimElement.getStyle().setBorderStyle(BorderStyle.NONE);
  270. shimElement.setTabIndex(-1);
  271. shimElement.setFrameBorder(0);
  272. shimElement.setMarginHeight(0);
  273. }
  274. return shimElement;
  275. }
  276. private int getActualTop() {
  277. int y = getAbsoluteTop();
  278. /* This is needed for IE7 at least */
  279. // Account for the difference between absolute position and the
  280. // body's positioning context.
  281. y -= Document.get().getBodyOffsetTop();
  282. y -= adjustByRelativeTopBodyMargin();
  283. return y;
  284. }
  285. private int getActualLeft() {
  286. int x = getAbsoluteLeft();
  287. /* This is needed for IE7 at least */
  288. // Account for the difference between absolute position and the
  289. // body's positioning context.
  290. x -= Document.get().getBodyOffsetLeft();
  291. x -= adjustByRelativeLeftBodyMargin();
  292. return x;
  293. }
  294. private static int adjustByRelativeTopBodyMargin() {
  295. if (topFix == -1) {
  296. topFix = detectRelativeBodyFixes("top");
  297. }
  298. return topFix;
  299. }
  300. private static native int detectRelativeBodyFixes(String axis)
  301. /*-{
  302. try {
  303. var b = $wnd.document.body;
  304. var cstyle = b.currentStyle ? b.currentStyle : getComputedStyle(b);
  305. if(cstyle && cstyle.position == 'relative') {
  306. return b.getBoundingClientRect()[axis];
  307. }
  308. } catch(e){}
  309. return 0;
  310. }-*/;
  311. private static int adjustByRelativeLeftBodyMargin() {
  312. if (leftFix == -1) {
  313. leftFix = detectRelativeBodyFixes("left");
  314. }
  315. return leftFix;
  316. }
  317. /*
  318. * A "thread local" of sorts, set temporarily so that OverlayImpl knows
  319. * which Overlay is using it, so that it can be attached to the correct
  320. * overlay container.
  321. *
  322. * TODO this is a strange pattern that we should get rid of when possible.
  323. */
  324. protected static Overlay current;
  325. @Override
  326. public void show() {
  327. current = this;
  328. maybeShowWithAnimation();
  329. if (isAnimationEnabled()) {
  330. new ResizeAnimation().run(POPUP_PANEL_ANIMATION_DURATION);
  331. } else {
  332. positionOrSizeUpdated(1.0);
  333. }
  334. current = null;
  335. }
  336. private boolean fitInWindow = false;
  337. private boolean maybeShowWithAnimation() {
  338. boolean isAttached = isAttached() && isShowing();
  339. super.show();
  340. // Don't animate if already visible
  341. if (isAttached) {
  342. return false;
  343. } else {
  344. // Check if animations are used
  345. setVisible(false);
  346. addStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
  347. ComputedStyle cs = new ComputedStyle(getElement());
  348. String animationName = AnimationUtil.getAnimationName(cs);
  349. if (animationName == null) {
  350. animationName = "";
  351. }
  352. setVisible(true);
  353. if (animationName.contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  354. // Disable GWT PopupPanel animation if used
  355. setAnimationEnabled(false);
  356. AnimationUtil.addAnimationEndListener(getElement(),
  357. new AnimationEndListener() {
  358. @Override
  359. public void onAnimationEnd(NativeEvent event) {
  360. String animationName = AnimationUtil
  361. .getAnimationName(event);
  362. if (animationName.contains(
  363. ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  364. boolean removed = AnimationUtil
  365. .removeAnimationEndListener(
  366. getElement(), this);
  367. assert removed : "Animation end listener was not removed";
  368. removeStyleDependentName(
  369. ADDITIONAL_CLASSNAME_ANIMATE_IN);
  370. }
  371. }
  372. });
  373. return true;
  374. } else {
  375. removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
  376. return false;
  377. }
  378. }
  379. }
  380. @Override
  381. protected void onDetach() {
  382. super.onDetach();
  383. // Always ensure shadow is removed when the overlay is removed.
  384. removeShimElement();
  385. }
  386. @Override
  387. public void setVisible(boolean visible) {
  388. super.setVisible(visible);
  389. if (isShimElementEnabled()) {
  390. shimElement.getStyle().setProperty("visibility",
  391. visible ? "visible" : "hidden");
  392. }
  393. }
  394. @Override
  395. public void setWidth(String width) {
  396. super.setWidth(width);
  397. positionOrSizeUpdated(1.0);
  398. }
  399. @Override
  400. public void setHeight(String height) {
  401. super.setHeight(height);
  402. positionOrSizeUpdated(1.0);
  403. }
  404. /**
  405. * Extending classes should always call this method after they change the
  406. * size of overlay without using normal 'setWidth(String)' and
  407. * 'setHeight(String)' methods (if not calling super.setWidth/Height).
  408. *
  409. */
  410. public void positionOrSizeUpdated() {
  411. positionOrSizeUpdated(1.0);
  412. }
  413. /**
  414. * @deprecated Call {@link #positionOrSizeUpdated()} instead.
  415. */
  416. @Deprecated
  417. protected void updateShadowSizeAndPosition() {
  418. positionOrSizeUpdated();
  419. }
  420. /**
  421. * Recalculates proper position and dimensions for the shadow and shim
  422. * elements. Can be used to animate the related elements, using the
  423. * 'progress' parameter (used to animate the shadow in sync with GWT
  424. * PopupPanel's default animation 'PopupPanel.AnimationType.CENTER').
  425. *
  426. * @param progress
  427. * A value between 0.0 and 1.0, indicating the progress of the
  428. * animation (0=start, 1=end).
  429. */
  430. private void positionOrSizeUpdated(final double progress) {
  431. // Don't do anything if overlay element is not attached
  432. if (!isAttached()) {
  433. return;
  434. }
  435. // Calculate proper z-index
  436. int zIndex = -1;
  437. try {
  438. // Odd behaviour with Windows Hosted Mode forces us to use
  439. // this redundant try/catch block (See dev.vaadin.com #2011)
  440. zIndex = Integer.parseInt(getElement().getStyle().getZIndex());
  441. } catch (Exception ignore) {
  442. // Ignored, will cause no harm
  443. zIndex = 1000;
  444. }
  445. if (zIndex == -1) {
  446. zIndex = Z_INDEX;
  447. }
  448. // Calculate position and size
  449. if (BrowserInfo.get().isIE()) {
  450. // Shake IE
  451. getOffsetHeight();
  452. getOffsetWidth();
  453. }
  454. if (needsShimElement()) {
  455. PositionAndSize positionAndSize = new PositionAndSize(
  456. getActualLeft(), getActualTop(), getOffsetWidth(),
  457. getOffsetHeight());
  458. // Animate the size
  459. positionAndSize.setAnimationFromCenterProgress(progress);
  460. Element container = getElement().getParentElement();
  461. if (needsShimElement()) {
  462. updateShimPosition(positionAndSize);
  463. if (shimElement.getParentElement() == null) {
  464. container.insertBefore(shimElement, getElement());
  465. }
  466. }
  467. }
  468. }
  469. private void updateShimPosition(PositionAndSize positionAndSize) {
  470. updatePositionAndSize(getShimElement(), positionAndSize);
  471. }
  472. /**
  473. * Returns true if we should add a shim iframe below the overlay to deal
  474. * with zindex issues with PDFs and applets. Can be overriden to disable
  475. * shim iframes if they are not needed.
  476. *
  477. * @return true if a shim iframe should be added, false otherwise
  478. */
  479. protected boolean needsShimElement() {
  480. BrowserInfo info = BrowserInfo.get();
  481. return info.isIE() && info.isBrowserVersionNewerOrEqual(8, 0);
  482. }
  483. private void updatePositionAndSize(Element e,
  484. PositionAndSize positionAndSize) {
  485. e.getStyle().setLeft(positionAndSize.getLeft(), Unit.PX);
  486. e.getStyle().setTop(positionAndSize.getTop(), Unit.PX);
  487. e.getStyle().setWidth(positionAndSize.getWidth(), Unit.PX);
  488. e.getStyle().setHeight(positionAndSize.getHeight(), Unit.PX);
  489. }
  490. protected class ResizeAnimation extends Animation {
  491. @Override
  492. protected void onUpdate(double progress) {
  493. positionOrSizeUpdated(progress);
  494. }
  495. }
  496. /**
  497. * Get owner (Widget that made this Overlay, not the layout parent) of
  498. * Overlay
  499. *
  500. * @return Owner (creator) or null if not defined
  501. */
  502. public Widget getOwner() {
  503. return owner;
  504. }
  505. /**
  506. * Set owner (Widget that made this Overlay, not the layout parent) of
  507. * Overlay
  508. *
  509. * @param owner
  510. * Owner (creator) of Overlay
  511. */
  512. public void setOwner(Widget owner) {
  513. this.owner = owner;
  514. }
  515. /**
  516. * Gets the 'overlay container' element.
  517. *
  518. * @return the overlay container element
  519. */
  520. public com.google.gwt.user.client.Element getOverlayContainer() {
  521. return RootPanel.get().getElement();
  522. }
  523. @Override
  524. public void center() {
  525. super.center();
  526. // Some devices can be zoomed in, we should center to the visual
  527. // viewport for those devices
  528. BrowserInfo b = BrowserInfo.get();
  529. if (b.isAndroid() || b.isIOS()) {
  530. int left = (getVisualViewportWidth() - getOffsetWidth()) >> 1;
  531. int top = (getVisualViewportHeight() - getOffsetHeight()) >> 1;
  532. setPopupPosition(Math.max(Window.getScrollLeft() + left, 0),
  533. Math.max(Window.getScrollTop() + top, 0));
  534. }
  535. }
  536. /**
  537. * Gets the visual viewport width, which is useful for e.g iOS where the
  538. * view can be zoomed in while keeping the layout viewport intact.
  539. *
  540. * Falls back to layout viewport; for those browsers/devices the difference
  541. * is that the scrollbar with is included (if there is a scrollbar).
  542. *
  543. * @since 7.0.7
  544. * @return
  545. */
  546. private int getVisualViewportWidth() {
  547. int w = (int) getSubpixelInnerWidth();
  548. if (w < 0) {
  549. return Window.getClientWidth();
  550. } else {
  551. return w;
  552. }
  553. }
  554. /**
  555. * Gets the visual viewport height, which is useful for e.g iOS where the
  556. * view can be zoomed in while keeping the layout viewport intact.
  557. *
  558. * Falls back to layout viewport; for those browsers/devices the difference
  559. * is that the scrollbar with is included (if there is a scrollbar).
  560. *
  561. * @since 7.0.7
  562. * @return
  563. */
  564. private int getVisualViewportHeight() {
  565. int h = (int) getSubpixelInnerHeight();
  566. if (h < 0) {
  567. return Window.getClientHeight();
  568. } else {
  569. return h;
  570. }
  571. }
  572. private native double getSubpixelInnerWidth()
  573. /*-{
  574. return $wnd.innerWidth !== undefined ? $wnd.innerWidth : -1;
  575. }-*/;
  576. private native double getSubpixelInnerHeight()
  577. /*-{
  578. return $wnd.innerHeight !== undefined ? $wnd.innerHeight :-1;
  579. }-*/;
  580. /*
  581. * (non-Javadoc)
  582. *
  583. * @see com.google.gwt.user.client.ui.PopupPanel#hide()
  584. */
  585. @Override
  586. public void hide() {
  587. hide(false);
  588. }
  589. /*
  590. * (non-Javadoc)
  591. *
  592. * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean)
  593. */
  594. @Override
  595. public void hide(final boolean autoClosed) {
  596. hide(autoClosed, true, true);
  597. }
  598. /**
  599. *
  600. * Hides the popup and detaches it from the page. This has no effect if it
  601. * is not currently showing. Animation-in, animation-out can be
  602. * enable/disabled for different use cases.
  603. *
  604. * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean)
  605. *
  606. * @param autoClosed
  607. * the value that will be passed to
  608. * {@link CloseHandler#onClose(CloseEvent)} when the popup is
  609. * closed
  610. * @param animateIn
  611. * enable/disable animate-in animation
  612. * @param animateOut
  613. * enable/disable animate-out animation
  614. * @since 7.3.7
  615. */
  616. public void hide(final boolean autoClosed, final boolean animateIn,
  617. final boolean animateOut) {
  618. if (animateIn
  619. && getStyleName().contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  620. AnimationUtil.addAnimationEndListener(getElement(),
  621. new AnimationEndListener() {
  622. @Override
  623. public void onAnimationEnd(NativeEvent event) {
  624. if (AnimationUtil.getAnimationName(event).contains(
  625. ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  626. boolean removed = AnimationUtil
  627. .removeAnimationEndListener(
  628. getElement(), this);
  629. assert removed : "Animation end listener was not removed";
  630. reallyHide(autoClosed);
  631. }
  632. }
  633. });
  634. } else {
  635. // Check if animations are used
  636. addStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  637. ComputedStyle cs = new ComputedStyle(getElement());
  638. String animationName = AnimationUtil.getAnimationName(cs);
  639. if (animationName == null) {
  640. animationName = "";
  641. }
  642. if (animateOut && animationName
  643. .contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
  644. // Disable GWT PopupPanel closing animation if used
  645. setAnimationEnabled(false);
  646. AnimationUtil.addAnimationEndListener(getElement(),
  647. new AnimationEndListener() {
  648. @Override
  649. public void onAnimationEnd(NativeEvent event) {
  650. String animationName = AnimationUtil
  651. .getAnimationName(event);
  652. if (animationName.contains(
  653. ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
  654. boolean removed = AnimationUtil
  655. .removeAnimationEndListener(
  656. getElement(), this);
  657. assert removed : "Animation end listener was not removed";
  658. // Remove both animation styles just in case
  659. removeStyleDependentName(
  660. ADDITIONAL_CLASSNAME_ANIMATE_IN);
  661. removeStyleDependentName(
  662. ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  663. reallyHide(autoClosed);
  664. }
  665. }
  666. });
  667. // No event previews should happen after the animation has
  668. // started
  669. Overlay.this.setPreviewingAllNativeEvents(false);
  670. } else {
  671. removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  672. reallyHide(autoClosed);
  673. }
  674. }
  675. }
  676. private void reallyHide(boolean autoClosed) {
  677. super.hide(autoClosed);
  678. for (Command c : runOnClose) {
  679. c.execute();
  680. }
  681. runOnClose.clear();
  682. }
  683. /**
  684. * Sets whether the overlay should be moved or shrunk to fit inside the
  685. * window.
  686. * <p>
  687. * When this is <code>false</code>, the default {@link PopupPanel} behavior
  688. * is used, which tries to position the popup primarly below and to the
  689. * right of a reference UIObject and, if there is not enough space, above or
  690. * to the left.
  691. * <p>
  692. * When this is <code>true</code>, the popup will be moved up/left in case
  693. * it does not fit on either side. If the popup is larger than the window,
  694. * it will be shrunk to fit and assume that scrolling e.g. using
  695. * <code>overflow:auto</code>, is taken care of by the overlay user.
  696. *
  697. * @since 7.6.6
  698. * @param fitInWindow
  699. * <code>true</code> to ensure that no part of the popup is
  700. * outside the visible view, <code>false</code> to use the
  701. * default {@link PopupPanel} behavior
  702. */
  703. public void setFitInWindow(boolean fitInWindow) {
  704. this.fitInWindow = fitInWindow;
  705. }
  706. /**
  707. * Checks whether the overlay should be moved or shrunk to fit inside the
  708. * window.
  709. *
  710. * @see #setFitInWindow(boolean)
  711. *
  712. * @since 7.6.6
  713. * @return <code>true</code> if the popup will be moved and/or shrunk to fit
  714. * inside the window, <code>false</code> otherwise
  715. */
  716. public boolean isFitInWindow() {
  717. return fitInWindow;
  718. }
  719. }