You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VOverlay.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. /*
  2. * Copyright 2000-2013 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 com.google.gwt.animation.client.Animation;
  18. import com.google.gwt.aria.client.Roles;
  19. import com.google.gwt.dom.client.Document;
  20. import com.google.gwt.dom.client.IFrameElement;
  21. import com.google.gwt.dom.client.Style;
  22. import com.google.gwt.dom.client.Style.BorderStyle;
  23. import com.google.gwt.dom.client.Style.Position;
  24. import com.google.gwt.dom.client.Style.Unit;
  25. import com.google.gwt.event.logical.shared.CloseEvent;
  26. import com.google.gwt.event.logical.shared.CloseHandler;
  27. import com.google.gwt.user.client.DOM;
  28. import com.google.gwt.user.client.Window;
  29. import com.google.gwt.user.client.ui.PopupPanel;
  30. import com.google.gwt.user.client.ui.RootPanel;
  31. import com.google.gwt.user.client.ui.Widget;
  32. import com.vaadin.client.ApplicationConnection;
  33. import com.vaadin.client.BrowserInfo;
  34. import com.vaadin.client.ComponentConnector;
  35. import com.vaadin.client.Util;
  36. import com.vaadin.client.VConsole;
  37. /**
  38. * In Vaadin UI this Overlay should always be used for all elements that
  39. * temporary float over other components like context menus etc. This is to deal
  40. * stacking order correctly with VWindow objects.
  41. */
  42. public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
  43. public static class PositionAndSize {
  44. private int left, top, width, height;
  45. public PositionAndSize(int left, int top, int width, int height) {
  46. super();
  47. setLeft(left);
  48. setTop(top);
  49. setWidth(width);
  50. setHeight(height);
  51. }
  52. public int getLeft() {
  53. return left;
  54. }
  55. public void setLeft(int left) {
  56. this.left = left;
  57. }
  58. public int getTop() {
  59. return top;
  60. }
  61. public void setTop(int top) {
  62. this.top = top;
  63. }
  64. public int getWidth() {
  65. return width;
  66. }
  67. public void setWidth(int width) {
  68. if (width < 0) {
  69. width = 0;
  70. }
  71. this.width = width;
  72. }
  73. public int getHeight() {
  74. return height;
  75. }
  76. public void setHeight(int height) {
  77. if (height < 0) {
  78. height = 0;
  79. }
  80. this.height = height;
  81. }
  82. public void setAnimationFromCenterProgress(double progress) {
  83. left += (int) (width * (1.0 - progress) / 2.0);
  84. top += (int) (height * (1.0 - progress) / 2.0);
  85. width = (int) (width * progress);
  86. height = (int) (height * progress);
  87. }
  88. }
  89. /*
  90. * The z-index value from where all overlays live. This can be overridden in
  91. * any extending class.
  92. */
  93. public static int Z_INDEX = 20000;
  94. private static int leftFix = -1;
  95. private static int topFix = -1;
  96. /**
  97. * Shadow element style. If an extending class wishes to use a different
  98. * style of shadow, it can use setShadowStyle(String) to give the shadow
  99. * element a new style name.
  100. */
  101. public static final String CLASSNAME_SHADOW = "v-shadow";
  102. /**
  103. * Style name for the overlay container element (see
  104. * {@link #getOverlayContainer()}
  105. */
  106. public static final String CLASSNAME_CONTAINER = "v-overlay-container";
  107. /*
  108. * The shadow element for this overlay.
  109. */
  110. private com.google.gwt.user.client.Element shadow;
  111. /*
  112. * The creator of this VOverlay (the widget that made the instance, not the
  113. * layout parent)
  114. */
  115. private Widget owner;
  116. /*
  117. * ApplicationConnection that this overlay belongs to, which is needed to
  118. * create the overlay in the correct container so that the correct styles
  119. * are applied. If not given, owner will be used to figure out, and as a
  120. * last fallback, the overlay is created w/o container, potentially missing
  121. * styles.
  122. */
  123. protected ApplicationConnection ac;
  124. /**
  125. * The shim iframe behind the overlay, allowing PDFs and applets to be
  126. * covered by overlays.
  127. */
  128. private IFrameElement shimElement;
  129. /**
  130. * The HTML snippet that is used to render the actual shadow. In consists of
  131. * nine different DIV-elements with the following class names:
  132. *
  133. * <pre>
  134. * .v-shadow[-stylename]
  135. * ----------------------------------------------
  136. * | .top-left | .top | .top-right |
  137. * |---------------|-----------|----------------|
  138. * | | | |
  139. * | .left | .center | .right |
  140. * | | | |
  141. * |---------------|-----------|----------------|
  142. * | .bottom-left | .bottom | .bottom-right |
  143. * ----------------------------------------------
  144. * </pre>
  145. *
  146. * See default theme 'shadow.css' for implementation example.
  147. */
  148. 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>";
  149. /**
  150. * Matches {@link PopupPanel}.ANIMATION_DURATION
  151. */
  152. private static final int POPUP_PANEL_ANIMATION_DURATION = 200;
  153. private boolean sinkShadowEvents = false;
  154. public VOverlay() {
  155. super();
  156. adjustZIndex();
  157. }
  158. public VOverlay(boolean autoHide) {
  159. super(autoHide);
  160. adjustZIndex();
  161. }
  162. public VOverlay(boolean autoHide, boolean modal) {
  163. super(autoHide, modal);
  164. adjustZIndex();
  165. }
  166. public VOverlay(boolean autoHide, boolean modal, boolean showShadow) {
  167. super(autoHide, modal);
  168. setShadowEnabled(showShadow);
  169. adjustZIndex();
  170. }
  171. /**
  172. * Method to controle whether DOM elements for shadow are added. With this
  173. * method subclasses can control displaying of shadow also after the
  174. * constructor.
  175. *
  176. * @param enabled
  177. * true if shadow should be displayed
  178. */
  179. protected void setShadowEnabled(boolean enabled) {
  180. if (enabled != isShadowEnabled()) {
  181. if (enabled) {
  182. shadow = DOM.createDiv();
  183. shadow.setClassName(CLASSNAME_SHADOW);
  184. shadow.setInnerHTML(SHADOW_HTML);
  185. DOM.setStyleAttribute(shadow, "position", "absolute");
  186. addCloseHandler(this);
  187. } else {
  188. removeShadowIfPresent();
  189. shadow = null;
  190. }
  191. }
  192. }
  193. protected boolean isShadowEnabled() {
  194. return shadow != null;
  195. }
  196. protected boolean isShimElementEnabled() {
  197. return shimElement != null;
  198. }
  199. private void removeShimElement() {
  200. if (shimElement != null) {
  201. shimElement.removeFromParent();
  202. }
  203. }
  204. private void removeShadowIfPresent() {
  205. if (isShadowAttached()) {
  206. // Remove event listener from the shadow
  207. unsinkShadowEvents();
  208. shadow.removeFromParent();
  209. }
  210. }
  211. private boolean isShadowAttached() {
  212. return isShadowEnabled() && shadow.getParentElement() != null;
  213. }
  214. private void adjustZIndex() {
  215. setZIndex(Z_INDEX);
  216. }
  217. /**
  218. * Set the z-index (visual stack position) for this overlay.
  219. *
  220. * @param zIndex
  221. * The new z-index
  222. */
  223. protected void setZIndex(int zIndex) {
  224. DOM.setStyleAttribute(getElement(), "zIndex", "" + zIndex);
  225. if (isShadowEnabled()) {
  226. DOM.setStyleAttribute(shadow, "zIndex", "" + zIndex);
  227. }
  228. }
  229. @Override
  230. public void setPopupPosition(int left, int top) {
  231. // TODO, this should in fact be part of
  232. // Document.get().getBodyOffsetLeft/Top(). Would require overriding DOM
  233. // for all permutations. Now adding fix as margin instead of fixing
  234. // left/top because parent class saves the position.
  235. Style style = getElement().getStyle();
  236. style.setMarginLeft(-adjustByRelativeLeftBodyMargin(), Unit.PX);
  237. style.setMarginTop(-adjustByRelativeTopBodyMargin(), Unit.PX);
  238. super.setPopupPosition(left, top);
  239. positionOrSizeUpdated(isAnimationEnabled() ? 0 : 1);
  240. }
  241. private IFrameElement getShimElement() {
  242. if (shimElement == null && needsShimElement()) {
  243. shimElement = Document.get().createIFrameElement();
  244. // Insert shim iframe before the main overlay element. It does not
  245. // matter if it is in front or behind the shadow as we cannot put a
  246. // shim behind the shadow due to its transparency.
  247. shimElement.getStyle().setPosition(Position.ABSOLUTE);
  248. shimElement.getStyle().setBorderStyle(BorderStyle.NONE);
  249. shimElement.setTabIndex(-1);
  250. shimElement.setFrameBorder(0);
  251. shimElement.setMarginHeight(0);
  252. }
  253. return shimElement;
  254. }
  255. private int getActualTop() {
  256. int y = getAbsoluteTop();
  257. /* This is needed for IE7 at least */
  258. // Account for the difference between absolute position and the
  259. // body's positioning context.
  260. y -= Document.get().getBodyOffsetTop();
  261. y -= adjustByRelativeTopBodyMargin();
  262. return y;
  263. }
  264. private int getActualLeft() {
  265. int x = getAbsoluteLeft();
  266. /* This is needed for IE7 at least */
  267. // Account for the difference between absolute position and the
  268. // body's positioning context.
  269. x -= Document.get().getBodyOffsetLeft();
  270. x -= adjustByRelativeLeftBodyMargin();
  271. return x;
  272. }
  273. private static int adjustByRelativeTopBodyMargin() {
  274. if (topFix == -1) {
  275. topFix = detectRelativeBodyFixes("top");
  276. }
  277. return topFix;
  278. }
  279. private native static int detectRelativeBodyFixes(String axis)
  280. /*-{
  281. try {
  282. var b = $wnd.document.body;
  283. var cstyle = b.currentStyle ? b.currentStyle : getComputedStyle(b);
  284. if(cstyle && cstyle.position == 'relative') {
  285. return b.getBoundingClientRect()[axis];
  286. }
  287. } catch(e){}
  288. return 0;
  289. }-*/;
  290. private static int adjustByRelativeLeftBodyMargin() {
  291. if (leftFix == -1) {
  292. leftFix = detectRelativeBodyFixes("left");
  293. }
  294. return leftFix;
  295. }
  296. /*
  297. * A "thread local" of sorts, set temporarily so that VOverlayImpl knows
  298. * which VOverlay is using it, so that it can be attached to the correct
  299. * overlay container.
  300. *
  301. * TODO this is a strange pattern that we should get rid of when possible.
  302. */
  303. protected static VOverlay current;
  304. @Override
  305. public void show() {
  306. current = this;
  307. super.show();
  308. if (isAnimationEnabled()) {
  309. new ResizeAnimation().run(POPUP_PANEL_ANIMATION_DURATION);
  310. } else {
  311. positionOrSizeUpdated(1.0);
  312. }
  313. current = null;
  314. }
  315. @Override
  316. protected void onDetach() {
  317. super.onDetach();
  318. // Always ensure shadow is removed when the overlay is removed.
  319. removeShadowIfPresent();
  320. removeShimElement();
  321. }
  322. @Override
  323. public void setVisible(boolean visible) {
  324. super.setVisible(visible);
  325. if (isShadowEnabled()) {
  326. shadow.getStyle().setProperty("visibility",
  327. visible ? "visible" : "hidden");
  328. }
  329. if (isShimElementEnabled()) {
  330. shimElement.getStyle().setProperty("visibility",
  331. visible ? "visible" : "hidden");
  332. }
  333. }
  334. @Override
  335. public void setWidth(String width) {
  336. super.setWidth(width);
  337. positionOrSizeUpdated(1.0);
  338. }
  339. @Override
  340. public void setHeight(String height) {
  341. super.setHeight(height);
  342. positionOrSizeUpdated(1.0);
  343. }
  344. /**
  345. * Sets the shadow style for this overlay. Will override any previous style
  346. * for the shadow. The default style name is defined by CLASSNAME_SHADOW.
  347. * The given style will be prefixed with CLASSNAME_SHADOW.
  348. *
  349. * @param style
  350. * The new style name for the shadow element. Will be prefixed by
  351. * CLASSNAME_SHADOW, e.g. style=='foobar' -> actual style
  352. * name=='v-shadow-foobar'.
  353. */
  354. protected void setShadowStyle(String style) {
  355. if (isShadowEnabled()) {
  356. shadow.setClassName(CLASSNAME_SHADOW + "-" + style);
  357. }
  358. }
  359. /**
  360. * Extending classes should always call this method after they change the
  361. * size of overlay without using normal 'setWidth(String)' and
  362. * 'setHeight(String)' methods (if not calling super.setWidth/Height).
  363. *
  364. */
  365. public void positionOrSizeUpdated() {
  366. positionOrSizeUpdated(1.0);
  367. }
  368. /**
  369. * @deprecated Call {@link #positionOrSizeUpdated()} instead.
  370. */
  371. @Deprecated
  372. protected void updateShadowSizeAndPosition() {
  373. positionOrSizeUpdated();
  374. }
  375. /**
  376. * Recalculates proper position and dimensions for the shadow and shim
  377. * elements. Can be used to animate the related elements, using the
  378. * 'progress' parameter (used to animate the shadow in sync with GWT
  379. * PopupPanel's default animation 'PopupPanel.AnimationType.CENTER').
  380. *
  381. * @param progress
  382. * A value between 0.0 and 1.0, indicating the progress of the
  383. * animation (0=start, 1=end).
  384. */
  385. private void positionOrSizeUpdated(final double progress) {
  386. // Don't do anything if overlay element is not attached
  387. if (!isAttached()) {
  388. return;
  389. }
  390. // Calculate proper z-index
  391. String zIndex = null;
  392. try {
  393. // Odd behaviour with Windows Hosted Mode forces us to use
  394. // this redundant try/catch block (See dev.vaadin.com #2011)
  395. zIndex = DOM.getStyleAttribute(getElement(), "zIndex");
  396. } catch (Exception ignore) {
  397. // Ignored, will cause no harm
  398. zIndex = "1000";
  399. }
  400. if (zIndex == null) {
  401. zIndex = "" + Z_INDEX;
  402. }
  403. // Calculate position and size
  404. if (BrowserInfo.get().isIE()) {
  405. // Shake IE
  406. getOffsetHeight();
  407. getOffsetWidth();
  408. }
  409. if (isShadowEnabled() || needsShimElement()) {
  410. PositionAndSize positionAndSize = new PositionAndSize(
  411. getActualLeft(), getActualTop(), getOffsetWidth(),
  412. getOffsetHeight());
  413. // Animate the size
  414. positionAndSize.setAnimationFromCenterProgress(progress);
  415. com.google.gwt.user.client.Element container = getElement()
  416. .getParentElement().cast();
  417. if (isShadowEnabled()) {
  418. updateShadowPosition(progress, zIndex, positionAndSize);
  419. if (shadow.getParentElement() == null) {
  420. container.insertBefore(shadow, getElement());
  421. sinkShadowEvents();
  422. }
  423. }
  424. if (needsShimElement()) {
  425. updateShimPosition(positionAndSize);
  426. if (shimElement.getParentElement() == null) {
  427. container.insertBefore(shimElement, getElement());
  428. }
  429. }
  430. }
  431. }
  432. private void updateShadowPosition(final double progress, String zIndex,
  433. PositionAndSize positionAndSize) {
  434. // Opera needs some shaking to get parts of the shadow showing
  435. // properly (ticket #2704)
  436. if (BrowserInfo.get().isOpera()) {
  437. // Clear the height of all middle elements
  438. DOM.getChild(shadow, 3).getStyle().setProperty("height", "auto");
  439. DOM.getChild(shadow, 4).getStyle().setProperty("height", "auto");
  440. DOM.getChild(shadow, 5).getStyle().setProperty("height", "auto");
  441. }
  442. updatePositionAndSize(shadow, positionAndSize);
  443. DOM.setStyleAttribute(shadow, "zIndex", zIndex);
  444. DOM.setStyleAttribute(shadow, "display", progress < 0.9 ? "none" : "");
  445. // Opera fix, part 2 (ticket #2704)
  446. if (BrowserInfo.get().isOpera()) {
  447. // We'll fix the height of all the middle elements
  448. DOM.getChild(shadow, 3)
  449. .getStyle()
  450. .setPropertyPx("height",
  451. DOM.getChild(shadow, 3).getOffsetHeight());
  452. DOM.getChild(shadow, 4)
  453. .getStyle()
  454. .setPropertyPx("height",
  455. DOM.getChild(shadow, 4).getOffsetHeight());
  456. DOM.getChild(shadow, 5)
  457. .getStyle()
  458. .setPropertyPx("height",
  459. DOM.getChild(shadow, 5).getOffsetHeight());
  460. }
  461. }
  462. private void updateShimPosition(PositionAndSize positionAndSize) {
  463. updatePositionAndSize(
  464. (com.google.gwt.user.client.Element) com.google.gwt.user.client.Element
  465. .as(getShimElement()), positionAndSize);
  466. }
  467. /**
  468. * Returns true if we should add a shim iframe below the overlay to deal
  469. * with zindex issues with PDFs and applets. Can be overriden to disable
  470. * shim iframes if they are not needed.
  471. *
  472. * @return true if a shim iframe should be added, false otherwise
  473. */
  474. protected boolean needsShimElement() {
  475. BrowserInfo info = BrowserInfo.get();
  476. return info.isIE() && info.isBrowserVersionNewerOrEqual(8, 0);
  477. }
  478. private void updatePositionAndSize(com.google.gwt.user.client.Element e,
  479. PositionAndSize positionAndSize) {
  480. e.getStyle().setLeft(positionAndSize.getLeft(), Unit.PX);
  481. e.getStyle().setTop(positionAndSize.getTop(), Unit.PX);
  482. e.getStyle().setWidth(positionAndSize.getWidth(), Unit.PX);
  483. e.getStyle().setHeight(positionAndSize.getHeight(), Unit.PX);
  484. }
  485. protected class ResizeAnimation extends Animation {
  486. @Override
  487. protected void onUpdate(double progress) {
  488. positionOrSizeUpdated(progress);
  489. }
  490. }
  491. @Override
  492. public void onClose(CloseEvent<PopupPanel> event) {
  493. removeShadowIfPresent();
  494. }
  495. @Override
  496. public void sinkEvents(int eventBitsToAdd) {
  497. super.sinkEvents(eventBitsToAdd);
  498. // Also sink events on the shadow if present
  499. sinkShadowEvents();
  500. }
  501. private void sinkShadowEvents() {
  502. if (isSinkShadowEvents() && isShadowAttached()) {
  503. // Sink the same events as the actual overlay has sunk
  504. DOM.sinkEvents(shadow, DOM.getEventsSunk(getElement()));
  505. // Send events to VOverlay.onBrowserEvent
  506. DOM.setEventListener(shadow, this);
  507. }
  508. }
  509. private void unsinkShadowEvents() {
  510. if (isShadowAttached()) {
  511. DOM.setEventListener(shadow, null);
  512. DOM.sinkEvents(shadow, 0);
  513. }
  514. }
  515. /**
  516. * Enables or disables sinking the events of the shadow to the same
  517. * onBrowserEvent as events to the actual overlay goes.
  518. *
  519. * Please note, that if you enable this, you can't assume that e.g.
  520. * event.getEventTarget returns an element inside the DOM structure of the
  521. * overlay
  522. *
  523. * @param sinkShadowEvents
  524. */
  525. protected void setSinkShadowEvents(boolean sinkShadowEvents) {
  526. this.sinkShadowEvents = sinkShadowEvents;
  527. if (sinkShadowEvents) {
  528. sinkShadowEvents();
  529. } else {
  530. unsinkShadowEvents();
  531. }
  532. }
  533. protected boolean isSinkShadowEvents() {
  534. return sinkShadowEvents;
  535. }
  536. /**
  537. * Get owner (Widget that made this VOverlay, not the layout parent) of
  538. * VOverlay
  539. *
  540. * @return Owner (creator) or null if not defined
  541. */
  542. public Widget getOwner() {
  543. return owner;
  544. }
  545. /**
  546. * Set owner (Widget that made this VOverlay, not the layout parent) of
  547. * VOverlay
  548. *
  549. * @param owner
  550. * Owner (creator) of VOverlay
  551. */
  552. public void setOwner(Widget owner) {
  553. this.owner = owner;
  554. }
  555. /**
  556. * Get the {@link ApplicationConnection} that this overlay belongs to. If
  557. * it's not set, {@link #getOwner()} is used to figure it out.
  558. *
  559. * @return
  560. */
  561. protected ApplicationConnection getApplicationConnection() {
  562. if (ac != null) {
  563. return ac;
  564. } else if (owner != null) {
  565. ComponentConnector c = Util.findConnectorFor(owner);
  566. if (c != null) {
  567. ac = c.getConnection();
  568. }
  569. return ac;
  570. } else {
  571. return null;
  572. }
  573. }
  574. /**
  575. * Gets the 'overlay container' element. Tries to find the current
  576. * {@link ApplicationConnection} using {@link #getApplicationConnection()}.
  577. *
  578. * @return the overlay container element for the current
  579. * {@link ApplicationConnection} or another element if the current
  580. * {@link ApplicationConnection} cannot be determined.
  581. */
  582. public com.google.gwt.user.client.Element getOverlayContainer() {
  583. ApplicationConnection ac = getApplicationConnection();
  584. if (ac == null) {
  585. // could not figure out which one we belong to, styling will
  586. // probably fail
  587. VConsole.error("Could not determine ApplicationConnection for Overlay. Overlay will be attached directly to the root panel");
  588. return RootPanel.get().getElement();
  589. } else {
  590. return getOverlayContainer(ac);
  591. }
  592. }
  593. /**
  594. * Gets the 'overlay container' element pertaining to the given
  595. * {@link ApplicationConnection}. Each overlay should be created in a
  596. * overlay container element, so that the correct theme and styles can be
  597. * applied.
  598. *
  599. * @param ac
  600. * A reference to {@link ApplicationConnection}
  601. * @return The overlay container
  602. */
  603. public static com.google.gwt.user.client.Element getOverlayContainer(
  604. ApplicationConnection ac) {
  605. String id = ac.getConfiguration().getRootPanelId();
  606. id = id += "-overlays";
  607. com.google.gwt.user.client.Element container = DOM.getElementById(id);
  608. if (container == null) {
  609. container = DOM.createDiv();
  610. container.setId(id);
  611. String styles = ac.getUIConnector().getWidget().getParent()
  612. .getStyleName();
  613. container.addClassName(styles);
  614. container.addClassName(CLASSNAME_CONTAINER);
  615. RootPanel.get().getElement().appendChild(container);
  616. }
  617. return container;
  618. }
  619. /**
  620. * Set the label of the container element, where tooltip, notification and
  621. * dialgs are added to.
  622. *
  623. * @param applicationConnection
  624. * the application connection for which to change the label
  625. * @param overlayContainerLabel
  626. * label for the container
  627. */
  628. public static void setOverlayContainerLabel(
  629. ApplicationConnection applicationConnection,
  630. String overlayContainerLabel) {
  631. Roles.getAlertRole().setAriaLabelProperty(
  632. VOverlay.getOverlayContainer(applicationConnection),
  633. overlayContainerLabel);
  634. }
  635. @Override
  636. public void center() {
  637. super.center();
  638. // Some devices can be zoomed in, we should center to the visual
  639. // viewport for those devices
  640. BrowserInfo b = BrowserInfo.get();
  641. if (b.isAndroid() || b.isIOS()) {
  642. int left = (getVisualViewportWidth() - getOffsetWidth()) >> 1;
  643. int top = (getVisualViewportHeight() - getOffsetHeight()) >> 1;
  644. setPopupPosition(Math.max(Window.getScrollLeft() + left, 0),
  645. Math.max(Window.getScrollTop() + top, 0));
  646. }
  647. }
  648. /**
  649. * Gets the visual viewport width, which is useful for e.g iOS where the
  650. * view can be zoomed in while keeping the layout viewport intact.
  651. *
  652. * Falls back to layout viewport; for those browsers/devices the difference
  653. * is that the scrollbar with is included (if there is a scrollbar).
  654. *
  655. * @since 7.0.7
  656. * @return
  657. */
  658. private int getVisualViewportWidth() {
  659. int w = (int) getSubpixelInnerWidth();
  660. if (w < 0) {
  661. return Window.getClientWidth();
  662. } else {
  663. return w;
  664. }
  665. }
  666. /**
  667. * Gets the visual viewport height, which is useful for e.g iOS where the
  668. * view can be zoomed in while keeping the layout viewport intact.
  669. *
  670. * Falls back to layout viewport; for those browsers/devices the difference
  671. * is that the scrollbar with is included (if there is a scrollbar).
  672. *
  673. * @since 7.0.7
  674. * @return
  675. */
  676. private int getVisualViewportHeight() {
  677. int h = (int) getSubpixelInnerHeight();
  678. if (h < 0) {
  679. return Window.getClientHeight();
  680. } else {
  681. return h;
  682. }
  683. }
  684. private native double getSubpixelInnerWidth()
  685. /*-{
  686. return $wnd.innerWidth !== undefined ? $wnd.innerWidth : -1;
  687. }-*/;
  688. private native double getSubpixelInnerHeight()
  689. /*-{
  690. return $wnd.innerHeight !== undefined ? $wnd.innerHeight :-1;
  691. }-*/;
  692. }