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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  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(() -> getWidget().setWidth(""));
  203. left = 0;
  204. }
  205. }
  206. int windowTop = Window.getScrollTop();
  207. int windowBottom = Window.getScrollTop() + Window.getClientHeight();
  208. int height = getOffsetHeight();
  209. int popupBottom = top + height;
  210. int popupBelowWindow = popupBottom - windowBottom;
  211. if (popupBelowWindow > 0) {
  212. // Popup is too large to fit
  213. top -= popupBelowWindow;
  214. if (top < 0) {
  215. // Would move above screen, shrink to fit in window
  216. setOuterHeightThroughWidget(windowBottom - windowTop);
  217. runOnClose.add(() -> getWidget().setHeight(""));
  218. top = 0;
  219. }
  220. }
  221. }
  222. // TODO, this should in fact be part of
  223. // Document.get().getBodyOffsetLeft/Top(). Would require overriding DOM
  224. // for all permutations. Now adding fix as margin instead of fixing
  225. // left/top because parent class saves the position.
  226. Style style = getElement().getStyle();
  227. style.setMarginLeft(-adjustByRelativeLeftBodyMargin(), Unit.PX);
  228. style.setMarginTop(-adjustByRelativeTopBodyMargin(), Unit.PX);
  229. super.setPopupPosition(left, top);
  230. positionOrSizeUpdated(isAnimationEnabled() ? 0 : 1);
  231. }
  232. private void setOuterHeightThroughWidget(int outerHeight) {
  233. getWidget().setHeight(outerHeight + "px");
  234. // Take margin/border/padding into account if needed
  235. // (the height is for the overlay root but we set it on the
  236. // widget)
  237. int adjustedHeight = outerHeight - (getOffsetHeight() - outerHeight);
  238. if (adjustedHeight != outerHeight) {
  239. getWidget().setHeight(adjustedHeight + "px");
  240. }
  241. }
  242. private void setOuterWidthThroughWidget(int outerWidth) {
  243. getWidget().setWidth(outerWidth + "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 adjustedWidth = outerWidth - (getOffsetWidth() - outerWidth);
  248. if (adjustedWidth != outerWidth) {
  249. getWidget().setWidth(adjustedWidth + "px");
  250. }
  251. }
  252. private IFrameElement getShimElement() {
  253. if (shimElement == null && needsShimElement()) {
  254. shimElement = Document.get().createIFrameElement();
  255. // Insert shim iframe before the main overlay element. It does not
  256. // matter if it is in front or behind the shadow as we cannot put a
  257. // shim behind the shadow due to its transparency.
  258. shimElement.getStyle().setPosition(Position.ABSOLUTE);
  259. shimElement.getStyle().setBorderStyle(BorderStyle.NONE);
  260. shimElement.setTabIndex(-1);
  261. shimElement.setFrameBorder(0);
  262. shimElement.setMarginHeight(0);
  263. }
  264. return shimElement;
  265. }
  266. private int getActualTop() {
  267. int y = getAbsoluteTop();
  268. /* This is needed for IE7 at least */
  269. // Account for the difference between absolute position and the
  270. // body's positioning context.
  271. y -= Document.get().getBodyOffsetTop();
  272. y -= adjustByRelativeTopBodyMargin();
  273. return y;
  274. }
  275. private int getActualLeft() {
  276. int x = getAbsoluteLeft();
  277. /* This is needed for IE7 at least */
  278. // Account for the difference between absolute position and the
  279. // body's positioning context.
  280. x -= Document.get().getBodyOffsetLeft();
  281. x -= adjustByRelativeLeftBodyMargin();
  282. return x;
  283. }
  284. private static int adjustByRelativeTopBodyMargin() {
  285. if (topFix == -1) {
  286. topFix = detectRelativeBodyFixes("top");
  287. }
  288. return topFix;
  289. }
  290. private static native int detectRelativeBodyFixes(String axis)
  291. /*-{
  292. try {
  293. var b = $wnd.document.body;
  294. var cstyle = b.currentStyle ? b.currentStyle : getComputedStyle(b);
  295. if (cstyle && cstyle.position == 'relative') {
  296. return b.getBoundingClientRect()[axis];
  297. }
  298. } catch(e) {}
  299. return 0;
  300. }-*/;
  301. private static int adjustByRelativeLeftBodyMargin() {
  302. if (leftFix == -1) {
  303. leftFix = detectRelativeBodyFixes("left");
  304. }
  305. return leftFix;
  306. }
  307. /*
  308. * A "thread local" of sorts, set temporarily so that OverlayImpl knows
  309. * which Overlay is using it, so that it can be attached to the correct
  310. * overlay container.
  311. *
  312. * TODO this is a strange pattern that we should get rid of when possible.
  313. */
  314. protected static Overlay current;
  315. @Override
  316. public void show() {
  317. current = this;
  318. maybeShowWithAnimation();
  319. if (isAnimationEnabled()) {
  320. new ResizeAnimation().run(POPUP_PANEL_ANIMATION_DURATION);
  321. } else {
  322. positionOrSizeUpdated(1.0);
  323. }
  324. current = null;
  325. }
  326. private boolean fitInWindow = false;
  327. private boolean maybeShowWithAnimation() {
  328. boolean isAttached = isAttached() && isShowing();
  329. super.show();
  330. // Don't animate if already visible
  331. if (isAttached) {
  332. return false;
  333. } else {
  334. // Check if animations are used
  335. setVisible(false);
  336. addStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
  337. ComputedStyle cs = new ComputedStyle(getElement());
  338. String animationName = AnimationUtil.getAnimationName(cs);
  339. if (animationName == null) {
  340. animationName = "";
  341. }
  342. setVisible(true);
  343. if (animationName.contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  344. // Disable GWT PopupPanel animation if used
  345. setAnimationEnabled(false);
  346. AnimationUtil.addAnimationEndListener(getElement(),
  347. new AnimationEndListener() {
  348. @Override
  349. public void onAnimationEnd(NativeEvent event) {
  350. String animationName = AnimationUtil
  351. .getAnimationName(event);
  352. if (animationName.contains(
  353. ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  354. boolean removed = AnimationUtil
  355. .removeAnimationEndListener(
  356. getElement(), this);
  357. assert removed : "Animation end listener was not removed";
  358. removeStyleDependentName(
  359. ADDITIONAL_CLASSNAME_ANIMATE_IN);
  360. }
  361. }
  362. });
  363. return true;
  364. } else {
  365. removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
  366. return false;
  367. }
  368. }
  369. }
  370. @Override
  371. protected void onDetach() {
  372. super.onDetach();
  373. // Always ensure shadow is removed when the overlay is removed.
  374. removeShimElement();
  375. }
  376. @Override
  377. public void setVisible(boolean visible) {
  378. super.setVisible(visible);
  379. if (isShimElementEnabled()) {
  380. shimElement.getStyle().setProperty("visibility",
  381. visible ? "visible" : "hidden");
  382. }
  383. }
  384. @Override
  385. public void setWidth(String width) {
  386. super.setWidth(width);
  387. positionOrSizeUpdated(1.0);
  388. }
  389. @Override
  390. public void setHeight(String height) {
  391. super.setHeight(height);
  392. positionOrSizeUpdated(1.0);
  393. }
  394. /**
  395. * Extending classes should always call this method after they change the
  396. * size of overlay without using normal 'setWidth(String)' and
  397. * 'setHeight(String)' methods (if not calling super.setWidth/Height).
  398. *
  399. */
  400. public void positionOrSizeUpdated() {
  401. positionOrSizeUpdated(1.0);
  402. }
  403. /**
  404. * @deprecated Call {@link #positionOrSizeUpdated()} instead.
  405. */
  406. @Deprecated
  407. protected void updateShadowSizeAndPosition() {
  408. positionOrSizeUpdated();
  409. }
  410. /**
  411. * Recalculates proper position and dimensions for the shadow and shim
  412. * elements. Can be used to animate the related elements, using the
  413. * 'progress' parameter (used to animate the shadow in sync with GWT
  414. * PopupPanel's default animation 'PopupPanel.AnimationType.CENTER').
  415. *
  416. * @param progress
  417. * A value between 0.0 and 1.0, indicating the progress of the
  418. * animation (0=start, 1=end).
  419. */
  420. private void positionOrSizeUpdated(final double progress) {
  421. // Don't do anything if overlay element is not attached
  422. if (!isAttached()) {
  423. return;
  424. }
  425. // Calculate proper z-index
  426. int zIndex = -1;
  427. try {
  428. // Odd behavior with Windows Hosted Mode forces us to use
  429. // this redundant try/catch block (See dev.vaadin.com #2011)
  430. zIndex = Integer.parseInt(getElement().getStyle().getZIndex());
  431. } catch (Exception ignore) {
  432. // Ignored, will cause no harm
  433. zIndex = 1000;
  434. }
  435. if (zIndex == -1) {
  436. zIndex = Z_INDEX;
  437. }
  438. // Calculate position and size
  439. if (BrowserInfo.get().isIE()) {
  440. // Shake IE
  441. getOffsetHeight();
  442. getOffsetWidth();
  443. }
  444. if (needsShimElement()) {
  445. PositionAndSize positionAndSize = new PositionAndSize(
  446. getActualLeft(), getActualTop(), getOffsetWidth(),
  447. getOffsetHeight());
  448. // Animate the size
  449. positionAndSize.setAnimationFromCenterProgress(progress);
  450. Element container = getElement().getParentElement();
  451. if (needsShimElement()) {
  452. updateShimPosition(positionAndSize);
  453. if (shimElement.getParentElement() == null) {
  454. container.insertBefore(shimElement, getElement());
  455. }
  456. }
  457. }
  458. }
  459. private void updateShimPosition(PositionAndSize positionAndSize) {
  460. updatePositionAndSize(getShimElement(), positionAndSize);
  461. }
  462. /**
  463. * Returns true if we should add a shim iframe below the overlay to deal
  464. * with zindex issues with PDFs and applets. Can be overridden to disable
  465. * shim iframes if they are not needed.
  466. *
  467. * @return true if a shim iframe should be added, false otherwise
  468. */
  469. protected boolean needsShimElement() {
  470. BrowserInfo info = BrowserInfo.get();
  471. return info.isIE() && info.isBrowserVersionNewerOrEqual(8, 0);
  472. }
  473. private void updatePositionAndSize(Element e,
  474. PositionAndSize positionAndSize) {
  475. e.getStyle().setLeft(positionAndSize.getLeft(), Unit.PX);
  476. e.getStyle().setTop(positionAndSize.getTop(), Unit.PX);
  477. e.getStyle().setWidth(positionAndSize.getWidth(), Unit.PX);
  478. e.getStyle().setHeight(positionAndSize.getHeight(), Unit.PX);
  479. }
  480. protected class ResizeAnimation extends Animation {
  481. @Override
  482. protected void onUpdate(double progress) {
  483. positionOrSizeUpdated(progress);
  484. }
  485. }
  486. /**
  487. * Get owner (Widget that made this Overlay, not the layout parent) of
  488. * Overlay.
  489. *
  490. * @return Owner (creator) or null if not defined
  491. */
  492. public Widget getOwner() {
  493. return owner;
  494. }
  495. /**
  496. * Set owner (Widget that made this Overlay, not the layout parent) of
  497. * Overlay.
  498. *
  499. * @param owner
  500. * Owner (creator) of Overlay
  501. */
  502. public void setOwner(Widget owner) {
  503. this.owner = owner;
  504. }
  505. /**
  506. * Gets the 'overlay container' element.
  507. *
  508. * @return the overlay container element
  509. */
  510. public com.google.gwt.user.client.Element getOverlayContainer() {
  511. return RootPanel.get().getElement();
  512. }
  513. @Override
  514. public void center() {
  515. super.center();
  516. // Some devices can be zoomed in, we should center to the visual
  517. // viewport for those devices
  518. BrowserInfo b = BrowserInfo.get();
  519. if (b.isAndroid() || b.isIOS()) {
  520. int left = (getVisualViewportWidth() - getOffsetWidth()) >> 1;
  521. int top = (getVisualViewportHeight() - getOffsetHeight()) >> 1;
  522. setPopupPosition(Math.max(Window.getScrollLeft() + left, 0),
  523. Math.max(Window.getScrollTop() + top, 0));
  524. }
  525. }
  526. /**
  527. * Gets the visual viewport width, which is useful for e.g iOS where the
  528. * view can be zoomed in while keeping the layout viewport intact.
  529. *
  530. * Falls back to layout viewport; for those browsers/devices the difference
  531. * is that the scrollbar with is included (if there is a scrollbar).
  532. *
  533. * @since 7.0.7
  534. * @return
  535. */
  536. private int getVisualViewportWidth() {
  537. int w = (int) getSubpixelInnerWidth();
  538. if (w < 0) {
  539. return Window.getClientWidth();
  540. } else {
  541. return w;
  542. }
  543. }
  544. /**
  545. * Gets the visual viewport height, which is useful for e.g iOS where the
  546. * view can be zoomed in while keeping the layout viewport intact.
  547. *
  548. * Falls back to layout viewport; for those browsers/devices the difference
  549. * is that the scrollbar with is included (if there is a scrollbar).
  550. *
  551. * @since 7.0.7
  552. * @return
  553. */
  554. private int getVisualViewportHeight() {
  555. int h = (int) getSubpixelInnerHeight();
  556. if (h < 0) {
  557. return Window.getClientHeight();
  558. } else {
  559. return h;
  560. }
  561. }
  562. private native double getSubpixelInnerWidth()
  563. /*-{
  564. return $wnd.innerWidth !== undefined ? $wnd.innerWidth : -1;
  565. }-*/;
  566. private native double getSubpixelInnerHeight()
  567. /*-{
  568. return $wnd.innerHeight !== undefined ? $wnd.innerHeight :-1;
  569. }-*/;
  570. /*
  571. * (non-Javadoc)
  572. *
  573. * @see com.google.gwt.user.client.ui.PopupPanel#hide()
  574. */
  575. @Override
  576. public void hide() {
  577. hide(false);
  578. }
  579. /*
  580. * (non-Javadoc)
  581. *
  582. * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean)
  583. */
  584. @Override
  585. public void hide(final boolean autoClosed) {
  586. hide(autoClosed, true, true);
  587. }
  588. /**
  589. *
  590. * Hides the popup and detaches it from the page. This has no effect if it
  591. * is not currently showing. Animation-in, animation-out can be
  592. * enable/disabled for different use cases.
  593. *
  594. * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean)
  595. *
  596. * @param autoClosed
  597. * the value that will be passed to
  598. * {@link CloseHandler#onClose(CloseEvent)} when the popup is
  599. * closed
  600. * @param animateIn
  601. * enable/disable animate-in animation
  602. * @param animateOut
  603. * enable/disable animate-out animation
  604. * @since 7.3.7
  605. */
  606. public void hide(final boolean autoClosed, final boolean animateIn,
  607. final boolean animateOut) {
  608. if (animateIn
  609. && getStyleName().contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  610. AnimationUtil.addAnimationEndListener(getElement(),
  611. new AnimationEndListener() {
  612. @Override
  613. public void onAnimationEnd(NativeEvent event) {
  614. if (AnimationUtil.getAnimationName(event).contains(
  615. ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
  616. boolean removed = AnimationUtil
  617. .removeAnimationEndListener(
  618. getElement(), this);
  619. assert removed : "Animation end listener was not removed";
  620. reallyHide(autoClosed);
  621. }
  622. }
  623. });
  624. } else {
  625. // Check if animations are used
  626. addStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  627. ComputedStyle cs = new ComputedStyle(getElement());
  628. String animationName = AnimationUtil.getAnimationName(cs);
  629. if (animationName == null) {
  630. animationName = "";
  631. }
  632. if (animateOut && animationName
  633. .contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
  634. // Disable GWT PopupPanel closing animation if used
  635. setAnimationEnabled(false);
  636. AnimationUtil.addAnimationEndListener(getElement(),
  637. new AnimationEndListener() {
  638. @Override
  639. public void onAnimationEnd(NativeEvent event) {
  640. String animationName = AnimationUtil
  641. .getAnimationName(event);
  642. if (animationName.contains(
  643. ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
  644. boolean removed = AnimationUtil
  645. .removeAnimationEndListener(
  646. getElement(), this);
  647. assert removed : "Animation end listener was not removed";
  648. // Remove both animation styles just in case
  649. removeStyleDependentName(
  650. ADDITIONAL_CLASSNAME_ANIMATE_IN);
  651. removeStyleDependentName(
  652. ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  653. reallyHide(autoClosed);
  654. }
  655. }
  656. });
  657. // No event previews should happen after the animation has
  658. // started
  659. Overlay.this.setPreviewingAllNativeEvents(false);
  660. } else {
  661. removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
  662. reallyHide(autoClosed);
  663. }
  664. }
  665. }
  666. private void reallyHide(boolean autoClosed) {
  667. super.hide(autoClosed);
  668. for (Command c : runOnClose) {
  669. c.execute();
  670. }
  671. runOnClose.clear();
  672. }
  673. /**
  674. * Sets whether the overlay should be moved or shrunk to fit inside the
  675. * window.
  676. * <p>
  677. * When this is <code>false</code>, the default {@link PopupPanel} behavior
  678. * is used, which tries to position the popup primarly below and to the
  679. * right of a reference UIObject and, if there is not enough space, above or
  680. * to the left.
  681. * <p>
  682. * When this is <code>true</code>, the popup will be moved up/left in case
  683. * it does not fit on either side. If the popup is larger than the window,
  684. * it will be shrunk to fit and assume that scrolling e.g. using
  685. * <code>overflow:auto</code>, is taken care of by the overlay user.
  686. *
  687. * @since 7.6.6
  688. * @param fitInWindow
  689. * <code>true</code> to ensure that no part of the popup is
  690. * outside the visible view, <code>false</code> to use the
  691. * default {@link PopupPanel} behavior
  692. */
  693. public void setFitInWindow(boolean fitInWindow) {
  694. this.fitInWindow = fitInWindow;
  695. }
  696. /**
  697. * Checks whether the overlay should be moved or shrunk to fit inside the
  698. * window.
  699. *
  700. * @see #setFitInWindow(boolean)
  701. *
  702. * @since 7.6.6
  703. * @return <code>true</code> if the popup will be moved and/or shrunk to fit
  704. * inside the window, <code>false</code> otherwise
  705. */
  706. public boolean isFitInWindow() {
  707. return fitInWindow;
  708. }
  709. }