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.

WindowConnector.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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.window;
  17. import java.util.logging.Logger;
  18. import com.google.gwt.core.client.Scheduler;
  19. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  20. import com.google.gwt.dom.client.Element;
  21. import com.google.gwt.dom.client.NativeEvent;
  22. import com.google.gwt.dom.client.Node;
  23. import com.google.gwt.dom.client.NodeList;
  24. import com.google.gwt.dom.client.Style;
  25. import com.google.gwt.dom.client.Style.Position;
  26. import com.google.gwt.dom.client.Style.Unit;
  27. import com.google.gwt.event.dom.client.ClickEvent;
  28. import com.google.gwt.event.dom.client.ClickHandler;
  29. import com.google.gwt.event.dom.client.DoubleClickEvent;
  30. import com.google.gwt.event.dom.client.DoubleClickHandler;
  31. import com.google.gwt.user.client.DOM;
  32. import com.google.gwt.user.client.Event;
  33. import com.google.gwt.user.client.Window;
  34. import com.vaadin.client.ApplicationConnection;
  35. import com.vaadin.client.BrowserInfo;
  36. import com.vaadin.client.ComponentConnector;
  37. import com.vaadin.client.ConnectorHierarchyChangeEvent;
  38. import com.vaadin.client.LayoutManager;
  39. import com.vaadin.client.Paintable;
  40. import com.vaadin.client.UIDL;
  41. import com.vaadin.client.WidgetUtil;
  42. import com.vaadin.client.communication.RpcProxy;
  43. import com.vaadin.client.communication.StateChangeEvent;
  44. import com.vaadin.client.ui.AbstractSingleComponentContainerConnector;
  45. import com.vaadin.client.ui.ClickEventHandler;
  46. import com.vaadin.client.ui.PostLayoutListener;
  47. import com.vaadin.client.ui.ShortcutActionHandler;
  48. import com.vaadin.client.ui.ShortcutActionHandler.BeforeShortcutActionListener;
  49. import com.vaadin.client.ui.SimpleManagedLayout;
  50. import com.vaadin.client.ui.VWindow;
  51. import com.vaadin.client.ui.layout.MayScrollChildren;
  52. import com.vaadin.shared.MouseEventDetails;
  53. import com.vaadin.shared.ui.Connect;
  54. import com.vaadin.shared.ui.window.WindowMode;
  55. import com.vaadin.shared.ui.window.WindowServerRpc;
  56. import com.vaadin.shared.ui.window.WindowState;
  57. @Connect(value = com.vaadin.ui.Window.class)
  58. public class WindowConnector extends AbstractSingleComponentContainerConnector
  59. implements Paintable, BeforeShortcutActionListener,
  60. SimpleManagedLayout, PostLayoutListener, MayScrollChildren,
  61. WindowMoveHandler {
  62. private Node windowClone;
  63. private ClickEventHandler clickEventHandler = new ClickEventHandler(this) {
  64. @Override
  65. protected void fireClick(NativeEvent event,
  66. MouseEventDetails mouseDetails) {
  67. getRpcProxy(WindowServerRpc.class).click(mouseDetails);
  68. }
  69. };
  70. abstract class WindowEventHandler implements ClickHandler,
  71. DoubleClickHandler {
  72. }
  73. private WindowEventHandler maximizeRestoreClickHandler = new WindowEventHandler() {
  74. @Override
  75. public void onClick(ClickEvent event) {
  76. final Element target = event.getNativeEvent().getEventTarget()
  77. .cast();
  78. if (target == getWidget().maximizeRestoreBox) {
  79. // Click on maximize/restore box
  80. onMaximizeRestore();
  81. }
  82. }
  83. @Override
  84. public void onDoubleClick(DoubleClickEvent event) {
  85. final Element target = event.getNativeEvent().getEventTarget()
  86. .cast();
  87. if (getWidget().header.isOrHasChild(target)) {
  88. // Double click on header
  89. onMaximizeRestore();
  90. }
  91. }
  92. };
  93. @Override
  94. public boolean delegateCaptionHandling() {
  95. return false;
  96. }
  97. @Override
  98. protected void init() {
  99. super.init();
  100. VWindow window = getWidget();
  101. window.id = getConnectorId();
  102. window.client = getConnection();
  103. getLayoutManager().registerDependency(this,
  104. window.contentPanel.getElement());
  105. getLayoutManager().registerDependency(this, window.header);
  106. getLayoutManager().registerDependency(this, window.footer);
  107. window.addHandler(maximizeRestoreClickHandler, ClickEvent.getType());
  108. window.addHandler(maximizeRestoreClickHandler,
  109. DoubleClickEvent.getType());
  110. window.setOwner(getConnection().getUIConnector().getWidget());
  111. window.addMoveHandler(this);
  112. }
  113. @Override
  114. public void onUnregister() {
  115. LayoutManager lm = getLayoutManager();
  116. VWindow window = getWidget();
  117. lm.unregisterDependency(this, window.contentPanel.getElement());
  118. lm.unregisterDependency(this, window.header);
  119. lm.unregisterDependency(this, window.footer);
  120. }
  121. @Override
  122. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  123. VWindow window = getWidget();
  124. String connectorId = getConnectorId();
  125. // Workaround needed for Testing Tools (GWT generates window DOM
  126. // slightly different in different browsers).
  127. window.closeBox.setId(connectorId + "_window_close");
  128. window.maximizeRestoreBox
  129. .setId(connectorId + "_window_maximizerestore");
  130. window.visibilityChangesDisabled = true;
  131. if (!isRealUpdate(uidl)) {
  132. return;
  133. }
  134. window.visibilityChangesDisabled = false;
  135. // we may have actions
  136. for (int i = 0; i < uidl.getChildCount(); i++) {
  137. UIDL childUidl = uidl.getChildUIDL(i);
  138. if (childUidl.getTag().equals("actions")) {
  139. if (window.shortcutHandler == null) {
  140. window.shortcutHandler = new ShortcutActionHandler(
  141. connectorId, client);
  142. }
  143. window.shortcutHandler.updateActionMap(childUidl);
  144. }
  145. }
  146. if (uidl.hasAttribute("bringToFront")) {
  147. /*
  148. * Focus as a side-effect. Will be overridden by
  149. * ApplicationConnection if another component was focused by the
  150. * server side.
  151. */
  152. window.contentPanel.focus();
  153. window.bringToFrontSequence = uidl.getIntAttribute("bringToFront");
  154. VWindow.deferOrdering();
  155. }
  156. }
  157. @Override
  158. public void updateCaption(ComponentConnector component) {
  159. // NOP, window has own caption, layout caption not rendered
  160. }
  161. @Override
  162. public void onBeforeShortcutAction(Event e) {
  163. // NOP, nothing to update just avoid workaround ( causes excess
  164. // blur/focus )
  165. }
  166. @Override
  167. public VWindow getWidget() {
  168. return (VWindow) super.getWidget();
  169. }
  170. @Override
  171. public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
  172. // We always have 1 child, unless the child is hidden
  173. getWidget().contentPanel.setWidget(getContentWidget());
  174. if (getParent() == null && windowClone != null) {
  175. // If the window is removed from the UI, add the copy of the
  176. // contents to the window (in case of an 'out-animation')
  177. getWidget().getElement().removeAllChildren();
  178. getWidget().getElement().appendChild(windowClone);
  179. // Clean reference
  180. windowClone = null;
  181. }
  182. }
  183. @Override
  184. public void layout() {
  185. LayoutManager lm = getLayoutManager();
  186. VWindow window = getWidget();
  187. ComponentConnector content = getContent();
  188. boolean hasContent = (content != null);
  189. Element contentElement = window.contentPanel.getElement();
  190. Style contentStyle = window.contents.getStyle();
  191. int headerHeight = lm.getOuterHeight(window.header);
  192. contentStyle.setPaddingTop(headerHeight, Unit.PX);
  193. contentStyle.setMarginTop(-headerHeight, Unit.PX);
  194. int footerHeight = lm.getOuterHeight(window.footer);
  195. contentStyle.setPaddingBottom(footerHeight, Unit.PX);
  196. contentStyle.setMarginBottom(-footerHeight, Unit.PX);
  197. int minWidth = lm.getOuterWidth(window.header)
  198. - lm.getInnerWidth(window.header);
  199. int minHeight = footerHeight + headerHeight;
  200. getWidget().getElement().getStyle().setPropertyPx("minWidth", minWidth);
  201. getWidget().getElement().getStyle()
  202. .setPropertyPx("minHeight", minHeight);
  203. /*
  204. * Must set absolute position if the child has relative height and
  205. * there's a chance of horizontal scrolling as some browsers will
  206. * otherwise not take the scrollbar into account when calculating the
  207. * height.
  208. */
  209. if (hasContent) {
  210. Element layoutElement = content.getWidget().getElement();
  211. Style childStyle = layoutElement.getStyle();
  212. // IE8 needs some hackery to measure its content correctly
  213. WidgetUtil.forceIE8Redraw(layoutElement);
  214. if (content.isRelativeHeight() && !BrowserInfo.get().isIE9()) {
  215. childStyle.setPosition(Position.ABSOLUTE);
  216. Style wrapperStyle = contentElement.getStyle();
  217. if (window.getElement().getStyle().getWidth().length() == 0
  218. && !content.isRelativeWidth()) {
  219. /*
  220. * Need to lock width to make undefined width work even with
  221. * absolute positioning
  222. */
  223. int contentWidth = lm.getOuterWidth(layoutElement);
  224. wrapperStyle.setWidth(contentWidth, Unit.PX);
  225. } else {
  226. wrapperStyle.clearWidth();
  227. }
  228. } else {
  229. childStyle.clearPosition();
  230. }
  231. }
  232. }
  233. @Override
  234. public void postLayout() {
  235. VWindow window = getWidget();
  236. if (!window.isAttached()) {
  237. Logger.getLogger(WindowConnector.class.getName()).warning(
  238. "Called postLayout to detached Window.");
  239. return;
  240. }
  241. if (window.centered && getState().windowMode != WindowMode.MAXIMIZED) {
  242. window.center();
  243. }
  244. window.positionOrSizeUpdated();
  245. if (getParent() != null) {
  246. // Take a copy of the contents, since the server will detach all
  247. // children of this window when it's closed, and the window will be
  248. // emptied during the following hierarchy update (we need to keep
  249. // the contents visible for the duration of a possible
  250. // 'out-animation')
  251. // Fix for #14645 and #14785 - as soon as we clone audio and video
  252. // tags, they start fetching data, and playing immediately in
  253. // background, in case autoplay attribute is present. Therefore we
  254. // have to replace them with stubs in the clone. And we can't just
  255. // erase them, because there are corresponding player widgets to
  256. // animate
  257. windowClone = cloneNodeFilteringMedia(getWidget().getElement()
  258. .getFirstChild());
  259. }
  260. }
  261. private Node cloneNodeFilteringMedia(Node node) {
  262. if (node instanceof Element) {
  263. Element old = (Element) node;
  264. if ("audio".equalsIgnoreCase(old.getTagName())
  265. || "video".equalsIgnoreCase(old.getTagName())) {
  266. if (!old.hasAttribute("controls")
  267. && "audio".equalsIgnoreCase(old.getTagName())) {
  268. return null; // nothing to animate, so we won't add this to
  269. // the clone
  270. }
  271. Element newEl = DOM.createElement(old.getTagName());
  272. if (old.hasAttribute("controls")) {
  273. newEl.setAttribute("controls", old.getAttribute("controls"));
  274. }
  275. if (old.hasAttribute("style")) {
  276. newEl.setAttribute("style", old.getAttribute("style"));
  277. }
  278. if (old.hasAttribute("class")) {
  279. newEl.setAttribute("class", old.getAttribute("class"));
  280. }
  281. return newEl;
  282. }
  283. }
  284. Node res = node.cloneNode(false);
  285. if (node.hasChildNodes()) {
  286. NodeList<Node> nl = node.getChildNodes();
  287. for (int i = 0; i < nl.getLength(); i++) {
  288. Node clone = cloneNodeFilteringMedia(nl.getItem(i));
  289. if (clone != null) {
  290. res.appendChild(clone);
  291. }
  292. }
  293. }
  294. return res;
  295. }
  296. @Override
  297. public WindowState getState() {
  298. return (WindowState) super.getState();
  299. }
  300. @Override
  301. public void onStateChanged(StateChangeEvent stateChangeEvent) {
  302. super.onStateChanged(stateChangeEvent);
  303. VWindow window = getWidget();
  304. WindowState state = getState();
  305. if (state.modal != window.vaadinModality) {
  306. window.setVaadinModality(!window.vaadinModality);
  307. }
  308. boolean resizeable = state.resizable
  309. && state.windowMode == WindowMode.NORMAL;
  310. window.setResizable(resizeable);
  311. window.resizeLazy = state.resizeLazy;
  312. window.setDraggable(state.draggable
  313. && state.windowMode == WindowMode.NORMAL);
  314. window.updateMaximizeRestoreClassName(state.resizable, state.windowMode);
  315. // Caption must be set before required header size is measured. If
  316. // the caption attribute is missing the caption should be cleared.
  317. String iconURL = null;
  318. if (getIconUri() != null) {
  319. iconURL = getIconUri();
  320. }
  321. window.setAssistivePrefix(state.assistivePrefix);
  322. window.setAssistivePostfix(state.assistivePostfix);
  323. window.setCaption(state.caption, iconURL, getState().captionAsHtml);
  324. window.setWaiAriaRole(getState().role);
  325. window.setAssistiveDescription(state.contentDescription);
  326. window.setTabStopEnabled(getState().assistiveTabStop);
  327. window.setTabStopTopAssistiveText(getState().assistiveTabStopTopText);
  328. window.setTabStopBottomAssistiveText(getState().assistiveTabStopBottomText);
  329. clickEventHandler.handleEventHandlerRegistration();
  330. window.immediate = state.immediate;
  331. window.setClosable(!isReadOnly());
  332. // initialize position from state
  333. updateWindowPosition();
  334. // setting scrollposition must happen after children is rendered
  335. window.contentPanel.setScrollPosition(state.scrollTop);
  336. window.contentPanel.setHorizontalScrollPosition(state.scrollLeft);
  337. // Center this window on screen if requested
  338. // This had to be here because we might not know the content size before
  339. // everything is painted into the window
  340. // centered is this is unset on move/resize
  341. window.centered = state.centered;
  342. // Ensure centering before setting visible (#16486)
  343. if (window.centered && getState().windowMode != WindowMode.MAXIMIZED) {
  344. Scheduler.get().scheduleFinally(new ScheduledCommand() {
  345. @Override
  346. public void execute() {
  347. getWidget().center();
  348. }
  349. });
  350. }
  351. window.setVisible(true);
  352. // ensure window is not larger than browser window
  353. if (window.getOffsetWidth() > Window.getClientWidth()) {
  354. window.setWidth(Window.getClientWidth() + "px");
  355. }
  356. if (window.getOffsetHeight() > Window.getClientHeight()) {
  357. window.setHeight(Window.getClientHeight() + "px");
  358. }
  359. }
  360. // Need to override default because of window mode
  361. @Override
  362. protected void updateComponentSize() {
  363. if (getState().windowMode == WindowMode.NORMAL) {
  364. super.updateComponentSize();
  365. } else if (getState().windowMode == WindowMode.MAXIMIZED) {
  366. super.updateComponentSize("100%", "100%");
  367. }
  368. }
  369. protected void updateWindowPosition() {
  370. VWindow window = getWidget();
  371. WindowState state = getState();
  372. if (state.windowMode == WindowMode.NORMAL) {
  373. // if centered, position handled in postLayout()
  374. if (!state.centered
  375. && (state.positionX >= 0 || state.positionY >= 0)) {
  376. // If both positions are negative, then
  377. // setWindowOrderAndPosition has already taken care of
  378. // positioning the window so it stacks with other windows
  379. window.setPopupPosition(state.positionX, state.positionY);
  380. }
  381. } else if (state.windowMode == WindowMode.MAXIMIZED) {
  382. window.setPopupPositionNoUpdate(0, 0);
  383. }
  384. }
  385. protected void updateWindowMode() {
  386. VWindow window = getWidget();
  387. WindowState state = getState();
  388. // update draggable on widget
  389. window.setDraggable(state.draggable
  390. && state.windowMode == WindowMode.NORMAL);
  391. // update resizable on widget
  392. window.setResizable(state.resizable
  393. && state.windowMode == WindowMode.NORMAL);
  394. updateComponentSize();
  395. updateWindowPosition();
  396. window.updateMaximizeRestoreClassName(state.resizable, state.windowMode);
  397. window.updateContentsSize();
  398. }
  399. protected void onMaximizeRestore() {
  400. WindowState state = getState();
  401. if (state.resizable) {
  402. if (state.windowMode == WindowMode.MAXIMIZED) {
  403. state.windowMode = WindowMode.NORMAL;
  404. } else {
  405. state.windowMode = WindowMode.MAXIMIZED;
  406. }
  407. updateWindowMode();
  408. VWindow window = getWidget();
  409. window.bringToFront();
  410. getRpcProxy(WindowServerRpc.class).windowModeChanged(
  411. state.windowMode);
  412. }
  413. }
  414. /**
  415. * Gives the WindowConnector an order number. As a side effect, moves the
  416. * window according to its order number so the windows are stacked. This
  417. * method should be called for each window in the order they should appear.
  418. */
  419. public void setWindowOrderAndPosition() {
  420. getWidget().setWindowOrderAndPosition();
  421. }
  422. @Override
  423. public boolean hasTooltip() {
  424. /*
  425. * Tooltip event handler always needed on the window widget to make sure
  426. * tooltips are properly hidden. (#11448)
  427. */
  428. return true;
  429. }
  430. @Override
  431. public void onWindowMove(WindowMoveEvent event) {
  432. RpcProxy.create(WindowServerRpc.class, this).windowMoved(
  433. event.getNewX(), event.getNewY());
  434. }
  435. }