Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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