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.

VWindow.java 52KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570
  1. /*
  2. * Copyright 2000-2018 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 static com.vaadin.client.WidgetUtil.isFocusedElementEditable;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.List;
  22. import com.google.gwt.aria.client.Id;
  23. import com.google.gwt.aria.client.RelevantValue;
  24. import com.google.gwt.aria.client.Roles;
  25. import com.google.gwt.core.client.Scheduler;
  26. import com.google.gwt.dom.client.Document;
  27. import com.google.gwt.dom.client.Element;
  28. import com.google.gwt.dom.client.NativeEvent;
  29. import com.google.gwt.dom.client.Style.Position;
  30. import com.google.gwt.dom.client.Style.Unit;
  31. import com.google.gwt.dom.client.Style.Visibility;
  32. import com.google.gwt.event.dom.client.BlurEvent;
  33. import com.google.gwt.event.dom.client.BlurHandler;
  34. import com.google.gwt.event.dom.client.FocusEvent;
  35. import com.google.gwt.event.dom.client.FocusHandler;
  36. import com.google.gwt.event.dom.client.KeyCodes;
  37. import com.google.gwt.event.dom.client.KeyDownEvent;
  38. import com.google.gwt.event.dom.client.KeyDownHandler;
  39. import com.google.gwt.event.dom.client.ScrollEvent;
  40. import com.google.gwt.event.dom.client.ScrollHandler;
  41. import com.google.gwt.event.shared.HandlerManager;
  42. import com.google.gwt.event.shared.HandlerRegistration;
  43. import com.google.gwt.user.client.DOM;
  44. import com.google.gwt.user.client.Event;
  45. import com.google.gwt.user.client.Event.NativePreviewHandler;
  46. import com.google.gwt.user.client.Window;
  47. import com.google.gwt.user.client.ui.Widget;
  48. import com.vaadin.client.ApplicationConnection;
  49. import com.vaadin.client.BrowserInfo;
  50. import com.vaadin.client.ComponentConnector;
  51. import com.vaadin.client.ConnectorMap;
  52. import com.vaadin.client.Focusable;
  53. import com.vaadin.client.HasComponentsConnector;
  54. import com.vaadin.client.LayoutManager;
  55. import com.vaadin.client.WidgetUtil;
  56. import com.vaadin.client.debug.internal.VDebugWindow;
  57. import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
  58. import com.vaadin.client.ui.aria.AriaHelper;
  59. import com.vaadin.client.ui.window.WindowConnector;
  60. import com.vaadin.client.ui.window.WindowMoveEvent;
  61. import com.vaadin.client.ui.window.WindowMoveHandler;
  62. import com.vaadin.client.ui.window.WindowOrderEvent;
  63. import com.vaadin.client.ui.window.WindowOrderHandler;
  64. import com.vaadin.shared.Connector;
  65. import com.vaadin.shared.EventId;
  66. import com.vaadin.shared.ui.window.WindowMode;
  67. import com.vaadin.shared.ui.window.WindowRole;
  68. /**
  69. * "Sub window" component.
  70. *
  71. * @author Vaadin Ltd
  72. */
  73. public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
  74. ScrollHandler, KeyDownHandler, FocusHandler, BlurHandler, Focusable {
  75. private static List<VWindow> windowOrder = new ArrayList<>();
  76. private static final HandlerManager WINDOW_ORDER_HANDLER = new HandlerManager(
  77. VWindow.class);
  78. private static boolean orderingDefered;
  79. public static final String CLASSNAME = "v-window";
  80. private static final String MODAL_WINDOW_OPEN_CLASSNAME = "v-modal-window-open";
  81. private static final int STACKING_OFFSET_PIXELS = 15;
  82. public static final int Z_INDEX = 10000;
  83. /** For internal use only. May be removed or replaced in the future. */
  84. public Element contents;
  85. /** For internal use only. May be removed or replaced in the future. */
  86. public Element header;
  87. /** For internal use only. May be removed or replaced in the future. */
  88. public Element footer;
  89. private Element resizeBox;
  90. /** For internal use only. May be removed or replaced in the future. */
  91. public final FocusableScrollPanel contentPanel = new FocusableScrollPanel();
  92. private boolean dragging;
  93. private int startX;
  94. private int startY;
  95. private int origX;
  96. private int origY;
  97. private boolean resizing;
  98. private int origW;
  99. private int origH;
  100. /** For internal use only. May be removed or replaced in the future. */
  101. public Element closeBox;
  102. /** For internal use only. May be removed or replaced in the future. */
  103. public Element maximizeRestoreBox;
  104. /** For internal use only. May be removed or replaced in the future. */
  105. public ApplicationConnection client;
  106. /** For internal use only. May be removed or replaced in the future. */
  107. public WindowConnector connector;
  108. /** For internal use only. May be removed or replaced in the future. */
  109. public String id;
  110. /** For internal use only. May be removed or replaced in the future. */
  111. public ShortcutActionHandler shortcutHandler;
  112. /**
  113. * Last known positionx read from UIDL or updated to application connection
  114. */
  115. private int uidlPositionX = -1;
  116. /**
  117. * Last known positiony read from UIDL or updated to application connection
  118. */
  119. private int uidlPositionY = -1;
  120. /** For internal use only. May be removed or replaced in the future. */
  121. public boolean vaadinModality = false;
  122. /** For internal use only. May be removed or replaced in the future. */
  123. public boolean resizable = true;
  124. private boolean draggable = true;
  125. /** For internal use only. May be removed or replaced in the future. */
  126. public boolean resizeLazy = false;
  127. private Element modalityCurtain;
  128. private Element draggingCurtain;
  129. private Element resizingCurtain;
  130. private Element headerText;
  131. private boolean closable = true;
  132. private Connector[] assistiveConnectors = new Connector[0];
  133. private String assistivePrefix;
  134. private String assistivePostfix;
  135. private Element topTabStop;
  136. private Element bottomTabStop;
  137. private NativePreviewHandler topEventBlocker;
  138. private NativePreviewHandler bottomEventBlocker;
  139. private NativePreviewHandler modalEventBlocker;
  140. private HandlerRegistration topBlockerRegistration;
  141. private HandlerRegistration bottomBlockerRegistration;
  142. private HandlerRegistration modalBlockerRegistration;
  143. // Prevents leaving the window with the Tab key when true
  144. private boolean doTabStop;
  145. /**
  146. * If centered (via UIDL), the window should stay in the centered -mode
  147. * until a position is received from the server, or the user moves or
  148. * resizes the window.
  149. * <p>
  150. * For internal use only. May be removed or replaced in the future.
  151. */
  152. public boolean centered = false;
  153. private Element wrapper;
  154. /** For internal use only. May be removed or replaced in the future. */
  155. public boolean visibilityChangesDisabled;
  156. /** For internal use only. May be removed or replaced in the future. */
  157. public int bringToFrontSequence = -1;
  158. private VLazyExecutor delayedContentsSizeUpdater = new VLazyExecutor(200,
  159. () -> updateContentsSize());
  160. public VWindow() {
  161. super(false, false); // no autohide, not modal
  162. Roles.getDialogRole().set(getElement());
  163. Roles.getDialogRole().setAriaRelevantProperty(getElement(),
  164. RelevantValue.ADDITIONS);
  165. constructDOM();
  166. contentPanel.addScrollHandler(this);
  167. contentPanel.addKeyDownHandler(this);
  168. contentPanel.addFocusHandler(this);
  169. contentPanel.addBlurHandler(this);
  170. addTransitionEndLayoutListener(getElement());
  171. }
  172. @Override
  173. protected void onAttach() {
  174. super.onAttach();
  175. /*
  176. * Stores the element that has focus in the application UI when the
  177. * window is opened, so it can be restored when the window closes.
  178. *
  179. * This is currently implemented for the case when one non-modal window
  180. * can be open at the same time, and the focus is not changed while the
  181. * window is open.
  182. */
  183. getApplicationConnection().getUIConnector().getWidget().storeFocus();
  184. /*
  185. * When this window gets reattached, set the tabstop to the previous
  186. * state.
  187. */
  188. setTabStopEnabled(doTabStop);
  189. }
  190. @Override
  191. protected void onDetach() {
  192. super.onDetach();
  193. /*
  194. * Restores the previously stored focused element.
  195. *
  196. * When the focus was changed outside the window while the window was
  197. * open, the originally stored element is restored.
  198. */
  199. getApplicationConnection().getUIConnector().getWidget()
  200. .focusStoredElement();
  201. removeTabBlockHandlers();
  202. // If you click while the window is being closed,
  203. // a new dragging curtain might be added and will
  204. // remain after detach. Theoretically a resize curtain can also remain
  205. // if you manage to click on the resize element
  206. hideDraggingCurtain();
  207. hideResizingCurtain();
  208. }
  209. private void addTabBlockHandlers() {
  210. if (topBlockerRegistration == null) {
  211. topBlockerRegistration = Event
  212. .addNativePreviewHandler(topEventBlocker);
  213. bottomBlockerRegistration = Event
  214. .addNativePreviewHandler(bottomEventBlocker);
  215. modalBlockerRegistration = Event
  216. .addNativePreviewHandler(modalEventBlocker);
  217. }
  218. }
  219. private void removeTabBlockHandlers() {
  220. if (topBlockerRegistration != null) {
  221. topBlockerRegistration.removeHandler();
  222. topBlockerRegistration = null;
  223. bottomBlockerRegistration.removeHandler();
  224. bottomBlockerRegistration = null;
  225. modalBlockerRegistration.removeHandler();
  226. modalBlockerRegistration = null;
  227. }
  228. }
  229. public void bringToFront() {
  230. bringToFront(true);
  231. }
  232. private void bringToFront(boolean notifyListeners) {
  233. int curIndex = getWindowOrder();
  234. if (curIndex + 1 < windowOrder.size()) {
  235. windowOrder.remove(this);
  236. windowOrder.add(this);
  237. for (; curIndex < windowOrder.size(); curIndex++) {
  238. VWindow window = windowOrder.get(curIndex);
  239. window.setWindowOrder(curIndex);
  240. }
  241. }
  242. if (notifyListeners) {
  243. fireOrderEvent();
  244. }
  245. }
  246. static void fireOrderEvent() {
  247. fireOrderEvent(windowOrder);
  248. }
  249. private void doFireOrderEvent() {
  250. List<VWindow> list = new ArrayList<>();
  251. list.add(this);
  252. fireOrderEvent(list);
  253. }
  254. private static void fireOrderEvent(List<VWindow> windows) {
  255. WINDOW_ORDER_HANDLER
  256. .fireEvent(new WindowOrderEvent(new ArrayList<>(windows)));
  257. }
  258. /**
  259. * Returns true if this window is the topmost VWindow
  260. *
  261. * @return
  262. */
  263. private boolean isActive() {
  264. return equals(getTopmostWindow());
  265. }
  266. private static VWindow getTopmostWindow() {
  267. if (!windowOrder.isEmpty()) {
  268. return windowOrder.get(windowOrder.size() - 1);
  269. }
  270. return null;
  271. }
  272. /** For internal use only. May be removed or replaced in the future. */
  273. public void setWindowOrderAndPosition() {
  274. // This cannot be done in the constructor as the widgets are created in
  275. // a different order than on they should appear on screen
  276. if (windowOrder.contains(this)) {
  277. // Already set
  278. return;
  279. }
  280. final int order = windowOrder.size();
  281. setWindowOrder(order);
  282. windowOrder.add(this);
  283. setPopupPosition(order * STACKING_OFFSET_PIXELS,
  284. order * STACKING_OFFSET_PIXELS);
  285. doFireOrderEvent();
  286. }
  287. private void setWindowOrder(int order) {
  288. setZIndex(order + Z_INDEX);
  289. }
  290. /**
  291. * Returns window position in list of opened and shown windows.
  292. *
  293. * @since 8.0
  294. */
  295. public final int getWindowOrder() {
  296. return windowOrder.indexOf(this);
  297. }
  298. @Override
  299. protected void setZIndex(int zIndex) {
  300. super.setZIndex(zIndex);
  301. if (vaadinModality) {
  302. getModalityCurtain().getStyle().setZIndex(zIndex);
  303. }
  304. }
  305. protected com.google.gwt.user.client.Element getModalityCurtain() {
  306. if (modalityCurtain == null) {
  307. modalityCurtain = DOM.createDiv();
  308. modalityCurtain.setClassName(CLASSNAME + "-modalitycurtain");
  309. }
  310. return DOM.asOld(modalityCurtain);
  311. }
  312. protected void constructDOM() {
  313. setStyleName(CLASSNAME);
  314. topTabStop = DOM.createDiv();
  315. DOM.setElementAttribute(topTabStop, "tabindex", "0");
  316. header = DOM.createDiv();
  317. DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader");
  318. headerText = DOM.createDiv();
  319. DOM.setElementProperty(headerText, "className", CLASSNAME + "-header");
  320. contents = DOM.createDiv();
  321. DOM.setElementProperty(contents, "className", CLASSNAME + "-contents");
  322. footer = DOM.createDiv();
  323. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
  324. resizeBox = DOM.createDiv();
  325. DOM.setElementProperty(resizeBox, "className",
  326. CLASSNAME + "-resizebox");
  327. closeBox = DOM.createDiv();
  328. maximizeRestoreBox = DOM.createDiv();
  329. DOM.setElementProperty(maximizeRestoreBox, "className",
  330. CLASSNAME + "-maximizebox");
  331. DOM.setElementAttribute(maximizeRestoreBox, "tabindex", "0");
  332. DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox");
  333. DOM.setElementAttribute(closeBox, "tabindex", "0");
  334. DOM.appendChild(footer, resizeBox);
  335. bottomTabStop = DOM.createDiv();
  336. DOM.setElementAttribute(bottomTabStop, "tabindex", "0");
  337. wrapper = DOM.createDiv();
  338. DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap");
  339. DOM.appendChild(wrapper, topTabStop);
  340. DOM.appendChild(wrapper, header);
  341. DOM.appendChild(header, maximizeRestoreBox);
  342. DOM.appendChild(header, closeBox);
  343. DOM.appendChild(header, headerText);
  344. DOM.appendChild(wrapper, contents);
  345. DOM.appendChild(wrapper, footer);
  346. DOM.appendChild(wrapper, bottomTabStop);
  347. DOM.appendChild(super.getContainerElement(), wrapper);
  348. sinkEvents(Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.TOUCHEVENTS
  349. | Event.ONCLICK | Event.ONLOSECAPTURE);
  350. setWidget(contentPanel);
  351. // Make the closebox accessible for assistive devices
  352. Roles.getButtonRole().set(closeBox);
  353. Roles.getButtonRole().setAriaLabelProperty(closeBox, "close button");
  354. // Make the maximizebox accessible for assistive devices
  355. Roles.getButtonRole().set(maximizeRestoreBox);
  356. Roles.getButtonRole().setAriaLabelProperty(maximizeRestoreBox,
  357. "maximize button");
  358. // Provide the title to assistive devices
  359. AriaHelper.ensureHasId(headerText);
  360. Roles.getDialogRole().setAriaLabelledbyProperty(getElement(),
  361. Id.of(headerText));
  362. // Handlers to Prevent tab to leave the window (by circulating focus)
  363. // and backspace to cause browser navigation
  364. topEventBlocker = event -> {
  365. if (!getElement().isOrHasChild(WidgetUtil.getFocusedElement())) {
  366. return;
  367. }
  368. NativeEvent nativeEvent = event.getNativeEvent();
  369. if (nativeEvent.getEventTarget().cast() == topTabStop
  370. && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB
  371. && nativeEvent.getShiftKey()) {
  372. nativeEvent.preventDefault();
  373. FocusUtil.focusOnLastFocusableElement(getElement());
  374. }
  375. if (nativeEvent.getEventTarget().cast() == topTabStop
  376. && nativeEvent.getKeyCode() == KeyCodes.KEY_BACKSPACE) {
  377. nativeEvent.preventDefault();
  378. }
  379. };
  380. bottomEventBlocker = event -> {
  381. if (!getElement().isOrHasChild(WidgetUtil.getFocusedElement())) {
  382. return;
  383. }
  384. NativeEvent nativeEvent = event.getNativeEvent();
  385. if (nativeEvent.getEventTarget().cast() == bottomTabStop
  386. && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB
  387. && !nativeEvent.getShiftKey()) {
  388. nativeEvent.preventDefault();
  389. FocusUtil.focusOnFirstFocusableElement(getElement());
  390. }
  391. if (nativeEvent.getEventTarget().cast() == bottomTabStop
  392. && nativeEvent.getKeyCode() == KeyCodes.KEY_BACKSPACE) {
  393. nativeEvent.preventDefault();
  394. }
  395. };
  396. // Handle modal window + tabbing when the focus is not inside the
  397. // window (custom tab order or tabbing in from browser url bar)
  398. modalEventBlocker = event -> {
  399. if (!vaadinModality
  400. || getElement().isOrHasChild(WidgetUtil.getFocusedElement())
  401. || (getTopmostWindow() != VWindow.this)) {
  402. return;
  403. }
  404. NativeEvent nativeEvent = event.getNativeEvent();
  405. if (nativeEvent.getType().equals("keyup")
  406. && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB) {
  407. nativeEvent.preventDefault();
  408. focus();
  409. }
  410. };
  411. }
  412. /**
  413. * Sets the message that is provided to users of assistive devices when the
  414. * user reaches the top of the window when leaving a window with the tab key
  415. * is prevented.
  416. * <p>
  417. * This message is not visible on the screen.
  418. *
  419. * @param topMessage
  420. * String provided when the user navigates with Shift-Tab keys to
  421. * the top of the window
  422. */
  423. public void setTabStopTopAssistiveText(String topMessage) {
  424. Roles.getNoteRole().setAriaLabelProperty(topTabStop, topMessage);
  425. }
  426. /**
  427. * Sets the message that is provided to users of assistive devices when the
  428. * user reaches the bottom of the window when leaving a window with the tab
  429. * key is prevented.
  430. * <p>
  431. * This message is not visible on the screen.
  432. *
  433. * @param bottomMessage
  434. * String provided when the user navigates with the Tab key to
  435. * the bottom of the window
  436. */
  437. public void setTabStopBottomAssistiveText(String bottomMessage) {
  438. Roles.getNoteRole().setAriaLabelProperty(bottomTabStop, bottomMessage);
  439. }
  440. /**
  441. * Gets the message that is provided to users of assistive devices when the
  442. * user reaches the top of the window when leaving a window with the tab key
  443. * is prevented.
  444. *
  445. * @return the top message
  446. */
  447. public String getTabStopTopAssistiveText() {
  448. return Roles.getNoteRole().getAriaLabelProperty(topTabStop);
  449. }
  450. /**
  451. * Gets the message that is provided to users of assistive devices when the
  452. * user reaches the bottom of the window when leaving a window with the tab
  453. * key is prevented.
  454. *
  455. * @return the bottom message
  456. */
  457. public String getTabStopBottomAssistiveText() {
  458. return Roles.getNoteRole().getAriaLabelProperty(bottomTabStop);
  459. }
  460. /**
  461. * Calling this method will defer ordering algorithm, to order windows based
  462. * on servers bringToFront and modality instructions. Non changed windows
  463. * will be left intact.
  464. * <p>
  465. * For internal use only. May be removed or replaced in the future.
  466. */
  467. public static void deferOrdering() {
  468. if (!orderingDefered) {
  469. orderingDefered = true;
  470. Scheduler.get().scheduleFinally(() -> {
  471. doServerSideOrdering();
  472. VNotification.bringNotificationsToFront();
  473. });
  474. }
  475. }
  476. private static void doServerSideOrdering() {
  477. orderingDefered = false;
  478. VWindow[] array = windowOrder.toArray(new VWindow[windowOrder.size()]);
  479. Arrays.sort(array, (o1, o2) -> {
  480. /*
  481. * Order by modality, then by bringtofront sequence.
  482. */
  483. if (o1.vaadinModality && !o2.vaadinModality) {
  484. return 1;
  485. }
  486. if (!o1.vaadinModality && o2.vaadinModality) {
  487. return -1;
  488. }
  489. if (o1.bringToFrontSequence > o2.bringToFrontSequence) {
  490. return 1;
  491. }
  492. if (o1.bringToFrontSequence < o2.bringToFrontSequence) {
  493. return -1;
  494. }
  495. return 0;
  496. });
  497. for (VWindow w : array) {
  498. if (w.bringToFrontSequence != -1 || w.vaadinModality) {
  499. w.bringToFront(false);
  500. w.bringToFrontSequence = -1;
  501. }
  502. }
  503. focusTopmostModalWindow();
  504. }
  505. private static void focusTopmostModalWindow() {
  506. VWindow topmost = getTopmostWindow();
  507. if (topmost != null && topmost.vaadinModality) {
  508. topmost.focus();
  509. }
  510. fireOrderEvent();
  511. }
  512. @Override
  513. public void setVisible(boolean visible) {
  514. /*
  515. * Visibility with VWindow works differently than with other Paintables
  516. * in Vaadin. Invisible VWindows are not attached to DOM at all. Flag is
  517. * used to avoid visibility call from
  518. * ApplicationConnection.updateComponent();
  519. */
  520. if (!visibilityChangesDisabled) {
  521. super.setVisible(visible);
  522. }
  523. if (visible && BrowserInfo.get()
  524. .requiresPositionAbsoluteOverflowAutoFix()) {
  525. /*
  526. * Shake up the DOM a bit to make the window shed unnecessary
  527. * scrollbars and resize correctly afterwards. The version fixing
  528. * ticket #11994 which was changing the size to 110% was replaced
  529. * with this due to ticket #12943
  530. */
  531. WidgetUtil
  532. .runWebkitOverflowAutoFix(contents.getFirstChildElement());
  533. Scheduler.get().scheduleFinally(() -> {
  534. List<ComponentConnector> childComponents = ((HasComponentsConnector) ConnectorMap
  535. .get(client).getConnector(this)).getChildComponents();
  536. if (!childComponents.isEmpty()) {
  537. LayoutManager layoutManager = getLayoutManager();
  538. layoutManager.setNeedsMeasure(childComponents.get(0));
  539. layoutManager.layoutNow();
  540. }
  541. });
  542. }
  543. }
  544. /** For internal use only. May be removed or replaced in the future. */
  545. public void setDraggable(boolean draggable) {
  546. if (this.draggable == draggable) {
  547. return;
  548. }
  549. this.draggable = draggable;
  550. setCursorProperties();
  551. }
  552. private void setCursorProperties() {
  553. if (!draggable) {
  554. header.getStyle().setProperty("cursor", "default");
  555. footer.getStyle().setProperty("cursor", "default");
  556. } else {
  557. header.getStyle().setProperty("cursor", "");
  558. footer.getStyle().setProperty("cursor", "");
  559. }
  560. }
  561. /**
  562. * Sets the closable state of the window. Additionally hides/shows the close
  563. * button according to the new state.
  564. *
  565. * @param closable
  566. * true if the window can be closed by the user
  567. */
  568. public void setClosable(boolean closable) {
  569. if (this.closable == closable) {
  570. return;
  571. }
  572. this.closable = closable;
  573. if (closable) {
  574. DOM.setElementProperty(closeBox, "className",
  575. CLASSNAME + "-closebox");
  576. } else {
  577. DOM.setElementProperty(closeBox, "className", CLASSNAME
  578. + "-closebox " + CLASSNAME + "-closebox-disabled");
  579. }
  580. }
  581. /**
  582. * Returns the closable state of the sub window. If the sub window is
  583. * closable a decoration (typically an X) is shown to the user. By clicking
  584. * on the X the user can close the window.
  585. *
  586. * @return true if the sub window is closable
  587. */
  588. protected boolean isClosable() {
  589. return closable;
  590. }
  591. @Override
  592. public void show() {
  593. if (!windowOrder.contains(this)) {
  594. // This is needed if the window is hidden and then shown again.
  595. // Otherwise this VWindow is added to windowOrder in the
  596. // constructor.
  597. windowOrder.add(this);
  598. }
  599. if (vaadinModality) {
  600. showModalityCurtain();
  601. }
  602. super.show();
  603. }
  604. @Override
  605. public void hide() {
  606. if (vaadinModality) {
  607. hideModalityCurtain();
  608. hideDraggingCurtain();
  609. hideResizingCurtain();
  610. }
  611. super.hide(false, true, false);
  612. int curIndex = getWindowOrder();
  613. // Remove window from windowOrder to avoid references being left
  614. // hanging.
  615. windowOrder.remove(curIndex);
  616. // Update the z-indices of any remaining windows
  617. List<VWindow> update = new ArrayList<>(
  618. windowOrder.size() - curIndex + 1);
  619. update.add(this);
  620. while (curIndex < windowOrder.size()) {
  621. VWindow window = windowOrder.get(curIndex);
  622. window.setWindowOrder(curIndex++);
  623. update.add(window);
  624. }
  625. focusTopmostModalWindow();
  626. fireOrderEvent(update);
  627. }
  628. /** For internal use only. May be removed or replaced in the future. */
  629. public void setVaadinModality(boolean modality) {
  630. vaadinModality = modality;
  631. if (vaadinModality) {
  632. getElement().setAttribute("aria-modal", "true");
  633. Roles.getDialogRole().set(getElement());
  634. if (isAttached()) {
  635. showModalityCurtain();
  636. }
  637. addTabBlockHandlers();
  638. deferOrdering();
  639. } else {
  640. getElement().removeAttribute("aria-modal");
  641. Roles.getDialogRole().remove(getElement());
  642. if (modalityCurtain != null) {
  643. if (isAttached()) {
  644. hideModalityCurtain();
  645. }
  646. modalityCurtain = null;
  647. }
  648. if (!doTabStop) {
  649. removeTabBlockHandlers();
  650. }
  651. }
  652. }
  653. private void showModalityCurtain() {
  654. getModalityCurtain().getStyle().setZIndex(getWindowOrder() + Z_INDEX);
  655. if (isShowing()) {
  656. getOverlayContainer().insertBefore(getModalityCurtain(),
  657. getElement());
  658. } else {
  659. getOverlayContainer().appendChild(getModalityCurtain());
  660. }
  661. Document.get().getBody().addClassName(MODAL_WINDOW_OPEN_CLASSNAME);
  662. }
  663. private void hideModalityCurtain() {
  664. Document.get().getBody().removeClassName(MODAL_WINDOW_OPEN_CLASSNAME);
  665. modalityCurtain.removeFromParent();
  666. // IE leaks memory in certain cases unless we release the reference
  667. // (#9197)
  668. modalityCurtain = null;
  669. }
  670. /*
  671. * Shows an empty div on top of all other content; used when moving, so that
  672. * iframes (etc) do not steal event.
  673. */
  674. private void showDraggingCurtain() {
  675. getElement().getParentElement().insertBefore(getDraggingCurtain(),
  676. getElement());
  677. }
  678. private void hideDraggingCurtain() {
  679. if (draggingCurtain != null) {
  680. draggingCurtain.removeFromParent();
  681. }
  682. }
  683. /*
  684. * Shows an empty div on top of all other content; used when resizing, so
  685. * that iframes (etc) do not steal event.
  686. */
  687. private void showResizingCurtain() {
  688. getElement().getParentElement().insertBefore(getResizingCurtain(),
  689. getElement());
  690. }
  691. private void hideResizingCurtain() {
  692. if (resizingCurtain != null) {
  693. resizingCurtain.removeFromParent();
  694. }
  695. }
  696. private Element getDraggingCurtain() {
  697. if (draggingCurtain == null) {
  698. draggingCurtain = createCurtain();
  699. draggingCurtain.setClassName(CLASSNAME + "-draggingCurtain");
  700. }
  701. return draggingCurtain;
  702. }
  703. private Element getResizingCurtain() {
  704. if (resizingCurtain == null) {
  705. resizingCurtain = createCurtain();
  706. resizingCurtain.setClassName(CLASSNAME + "-resizingCurtain");
  707. }
  708. return resizingCurtain;
  709. }
  710. private Element createCurtain() {
  711. Element curtain = DOM.createDiv();
  712. curtain.getStyle().setPosition(Position.ABSOLUTE);
  713. curtain.getStyle().setTop(0, Unit.PX);
  714. curtain.getStyle().setLeft(0, Unit.PX);
  715. curtain.getStyle().setWidth(100, Unit.PCT);
  716. curtain.getStyle().setHeight(100, Unit.PCT);
  717. curtain.getStyle().setZIndex(VOverlay.Z_INDEX);
  718. return curtain;
  719. }
  720. /** For internal use only. May be removed or replaced in the future. */
  721. public void setResizable(boolean resizability) {
  722. resizable = resizability;
  723. if (resizability) {
  724. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
  725. DOM.setElementProperty(resizeBox, "className",
  726. CLASSNAME + "-resizebox");
  727. } else {
  728. DOM.setElementProperty(footer, "className",
  729. CLASSNAME + "-footer " + CLASSNAME + "-footer-noresize");
  730. DOM.setElementProperty(resizeBox, "className", CLASSNAME
  731. + "-resizebox " + CLASSNAME + "-resizebox-disabled");
  732. }
  733. }
  734. public void updateMaximizeRestoreClassName(boolean visible,
  735. WindowMode windowMode) {
  736. String className;
  737. if (windowMode == WindowMode.MAXIMIZED) {
  738. className = CLASSNAME + "-restorebox";
  739. } else {
  740. className = CLASSNAME + "-maximizebox";
  741. }
  742. if (!visible) {
  743. className = className + " " + className + "-disabled";
  744. }
  745. maximizeRestoreBox.setClassName(className);
  746. }
  747. // TODO this will eventually be removed, currently used to avoid updating to
  748. // server side.
  749. public void setPopupPositionNoUpdate(int left, int top) {
  750. if (top < 0) {
  751. // ensure window is not moved out of browser window from top of the
  752. // screen
  753. top = 0;
  754. }
  755. super.setPopupPosition(left, top);
  756. }
  757. @Override
  758. public void setPopupPosition(int left, int top) {
  759. if (top < 0) {
  760. // ensure window is not moved out of browser window from top of the
  761. // screen
  762. top = 0;
  763. }
  764. super.setPopupPosition(left, top);
  765. if (left != uidlPositionX && client != null) {
  766. client.updateVariable(id, "positionx", left, false);
  767. uidlPositionX = left;
  768. }
  769. if (top != uidlPositionY && client != null) {
  770. client.updateVariable(id, "positiony", top, false);
  771. uidlPositionY = top;
  772. }
  773. }
  774. public void setCaption(String c) {
  775. setCaption(c, null);
  776. }
  777. public void setCaption(String c, String iconURL) {
  778. setCaption(c, iconURL, false);
  779. }
  780. public void setCaption(String c, String iconURL, boolean asHtml) {
  781. String html;
  782. if (asHtml) {
  783. html = c == null ? "" : c;
  784. } else {
  785. html = WidgetUtil.escapeHTML(c);
  786. }
  787. // Provide information to assistive device users that a sub window was
  788. // opened
  789. String prefix = "<span class='" + AriaHelper.ASSISTIVE_DEVICE_ONLY_STYLE
  790. + "'>" + assistivePrefix + "</span>";
  791. String postfix = "<span class='"
  792. + AriaHelper.ASSISTIVE_DEVICE_ONLY_STYLE + "'>"
  793. + assistivePostfix + "</span>";
  794. html = prefix + html + postfix;
  795. headerText.setInnerHTML(html);
  796. if (iconURL != null) {
  797. Icon icon = client.getIcon(iconURL);
  798. DOM.insertChild(headerText, icon.getElement(), 0);
  799. }
  800. }
  801. /**
  802. * Setter for the text for assistive devices the window caption is prefixed
  803. * with.
  804. *
  805. * @param assistivePrefix
  806. * the assistivePrefix to set
  807. */
  808. public void setAssistivePrefix(String assistivePrefix) {
  809. this.assistivePrefix = assistivePrefix;
  810. }
  811. /**
  812. * Getter for the text for assistive devices the window caption is prefixed
  813. * with.
  814. *
  815. * @return the assistivePrefix
  816. */
  817. public String getAssistivePrefix() {
  818. return assistivePrefix;
  819. }
  820. /**
  821. * Setter for the text for assistive devices the window caption is postfixed
  822. * with.
  823. *
  824. * @param assistivePostfix
  825. * the assistivePostfix to set
  826. */
  827. public void setAssistivePostfix(String assistivePostfix) {
  828. this.assistivePostfix = assistivePostfix;
  829. }
  830. /**
  831. * Getter for the text for assistive devices the window caption is postfixed
  832. * with.
  833. *
  834. * @return the assistivePostfix
  835. */
  836. public String getAssistivePostfix() {
  837. return assistivePostfix;
  838. }
  839. @Override
  840. protected com.google.gwt.user.client.Element getContainerElement() {
  841. // in GWT 1.5 this method is used in PopupPanel constructor
  842. if (contents == null) {
  843. return super.getContainerElement();
  844. }
  845. return DOM.asOld(contents);
  846. }
  847. private Event headerDragPending;
  848. @Override
  849. public void onBrowserEvent(final Event event) {
  850. boolean bubble = true;
  851. final int type = event.getTypeInt();
  852. final Element target = DOM.eventGetTarget(event);
  853. if (resizing || resizeBox == target) {
  854. onResizeEvent(event);
  855. bubble = false;
  856. } else if (isClosable() && target == closeBox) {
  857. if (type == Event.ONCLICK) {
  858. onCloseClick();
  859. }
  860. bubble = false;
  861. } else if (target == maximizeRestoreBox) {
  862. // handled in connector
  863. if (type != Event.ONCLICK) {
  864. bubble = false;
  865. }
  866. } else if (header.isOrHasChild(target) && !dragging) {
  867. // dblclick handled in connector
  868. if (type != Event.ONDBLCLICK && draggable) {
  869. if (type == Event.ONMOUSEDOWN || type == Event.ONTOUCHSTART) {
  870. /**
  871. * Prevents accidental selection of window caption or
  872. * content. (#12726)
  873. */
  874. event.preventDefault();
  875. headerDragPending = event;
  876. bubble = false;
  877. } else if (type == Event.ONMOUSEMOVE
  878. && headerDragPending != null) {
  879. // ie won't work unless this is set here
  880. dragging = true;
  881. onDragEvent(headerDragPending);
  882. onDragEvent(event);
  883. headerDragPending = null;
  884. bubble = false;
  885. } else if (type != Event.ONMOUSEMOVE) {
  886. // The event can propagate to the parent in case it is a
  887. // mouse move event. This is needed for tooltips to work in
  888. // header and footer, see Ticket #19073
  889. headerDragPending = null;
  890. bubble = false;
  891. } else {
  892. headerDragPending = null;
  893. }
  894. }
  895. if (type == Event.ONCLICK) {
  896. activateOnClick();
  897. }
  898. } else if (footer.isOrHasChild(target) && !dragging) {
  899. onDragEvent(event);
  900. if (type != Event.ONMOUSEMOVE) {
  901. // This is needed for tooltips to work in header and footer, see
  902. // Ticket #19073
  903. bubble = false;
  904. }
  905. } else if (dragging || !contents.isOrHasChild(target)) {
  906. onDragEvent(event);
  907. bubble = false;
  908. } else if (type == Event.ONCLICK) {
  909. activateOnClick();
  910. }
  911. /*
  912. * If clicking on other than the content, move focus to the window.
  913. * After that this windows e.g. gets all keyboard shortcuts.
  914. */
  915. if ((type == Event.ONMOUSEDOWN || type == Event.ONTOUCHSTART)
  916. && !contentPanel.getElement().isOrHasChild(target)
  917. && target != closeBox && target != maximizeRestoreBox) {
  918. contentPanel.focus();
  919. }
  920. if (!bubble) {
  921. event.stopPropagation();
  922. }
  923. // Super.onBrowserEvent takes care of Handlers added by the
  924. // ClickEventHandler
  925. super.onBrowserEvent(event);
  926. }
  927. private void activateOnClick() {
  928. // clicked inside window or inside header, ensure to be on top
  929. if (!isActive()) {
  930. bringToFront();
  931. }
  932. }
  933. private void onCloseClick() {
  934. // Send the close event to the server
  935. client.updateVariable(id, "close", true, true);
  936. }
  937. private void onResizeEvent(Event event) {
  938. if (resizable && WidgetUtil.isTouchEventOrLeftMouseButton(event)) {
  939. switch (event.getTypeInt()) {
  940. case Event.ONMOUSEDOWN:
  941. case Event.ONTOUCHSTART:
  942. if (!isActive()) {
  943. bringToFront();
  944. }
  945. showResizingCurtain();
  946. if (BrowserInfo.get().isIE()) {
  947. resizeBox.getStyle().setVisibility(Visibility.HIDDEN);
  948. }
  949. resizing = true;
  950. startX = WidgetUtil.getTouchOrMouseClientX(event);
  951. startY = WidgetUtil.getTouchOrMouseClientY(event);
  952. origW = getElement().getOffsetWidth();
  953. origH = getElement().getOffsetHeight();
  954. DOM.setCapture(getElement());
  955. event.preventDefault();
  956. break;
  957. case Event.ONMOUSEUP:
  958. case Event.ONTOUCHEND:
  959. setSize(event, true);
  960. case Event.ONTOUCHCANCEL:
  961. DOM.releaseCapture(getElement());
  962. case Event.ONLOSECAPTURE:
  963. hideResizingCurtain();
  964. if (BrowserInfo.get().isIE()) {
  965. resizeBox.getStyle().clearVisibility();
  966. }
  967. resizing = false;
  968. break;
  969. case Event.ONMOUSEMOVE:
  970. case Event.ONTOUCHMOVE:
  971. if (resizing) {
  972. centered = false;
  973. setSize(event, false);
  974. event.preventDefault();
  975. }
  976. break;
  977. default:
  978. event.preventDefault();
  979. break;
  980. }
  981. }
  982. }
  983. /**
  984. * TODO check if we need to support this with touch based devices.
  985. *
  986. * Checks if the cursor was inside the browser content area when the event
  987. * happened.
  988. *
  989. * @param event
  990. * The event to be checked
  991. * @return true, if the cursor is inside the browser content area
  992. *
  993. * false, otherwise
  994. */
  995. private boolean cursorInsideBrowserContentArea(Event event) {
  996. if (event.getClientX() < 0 || event.getClientY() < 0) {
  997. // Outside to the left or above
  998. return false;
  999. }
  1000. if (event.getClientX() > Window.getClientWidth()
  1001. || event.getClientY() > Window.getClientHeight()) {
  1002. // Outside to the right or below
  1003. return false;
  1004. }
  1005. return true;
  1006. }
  1007. private void setSize(Event event, boolean updateVariables) {
  1008. if (!cursorInsideBrowserContentArea(event)) {
  1009. // Only drag while cursor is inside the browser client area
  1010. return;
  1011. }
  1012. int w = WidgetUtil.getTouchOrMouseClientX(event) - startX + origW;
  1013. int h = WidgetUtil.getTouchOrMouseClientY(event) - startY + origH;
  1014. w = Math.max(w, getMinWidth());
  1015. h = Math.max(h, getMinHeight());
  1016. setWidth(w + "px");
  1017. setHeight(h + "px");
  1018. if (updateVariables) {
  1019. // sending width back always as pixels, no need for unit
  1020. client.updateVariable(id, "width", w, false);
  1021. client.updateVariable(id, "height", h, true);
  1022. }
  1023. if (updateVariables || !resizeLazy) {
  1024. // Resize has finished or is not lazy
  1025. updateContentsSize();
  1026. } else {
  1027. // Lazy resize - wait for a while before re-rendering contents
  1028. delayedContentsSizeUpdater.trigger();
  1029. }
  1030. }
  1031. private int getMinHeight() {
  1032. return getPixelValue(getElement().getStyle().getProperty("minHeight"));
  1033. }
  1034. private int getMinWidth() {
  1035. return getPixelValue(getElement().getStyle().getProperty("minWidth"));
  1036. }
  1037. private static int getPixelValue(String size) {
  1038. if (size == null || !size.endsWith("px")) {
  1039. return -1;
  1040. } else {
  1041. return Integer.parseInt(size.substring(0, size.length() - 2));
  1042. }
  1043. }
  1044. public void updateContentsSize() {
  1045. LayoutManager layoutManager = getLayoutManager();
  1046. layoutManager
  1047. .setNeedsMeasure(ConnectorMap.get(client).getConnector(this));
  1048. layoutManager.layoutNow();
  1049. }
  1050. private native void addTransitionEndLayoutListener(Element e)
  1051. /*-{
  1052. var self = this;
  1053. e.addEventListener("transitionend", function(e) {
  1054. if (e.propertyName == "width" || e.propertyName == 'height') {
  1055. $entry(function() {
  1056. self.@com.vaadin.client.ui.VWindow::updateContentsSize()();
  1057. })();
  1058. }
  1059. });
  1060. }-*/;
  1061. @Override
  1062. public void setWidth(String width) {
  1063. // Override PopupPanel which sets the width to the contents
  1064. getElement().getStyle().setProperty("width", width);
  1065. // Update v-has-width in case undefined window is resized
  1066. setStyleName("v-has-width", width != null && !width.isEmpty());
  1067. }
  1068. @Override
  1069. public void setHeight(String height) {
  1070. // Override PopupPanel which sets the height to the contents
  1071. getElement().getStyle().setProperty("height", height);
  1072. // Update v-has-height in case undefined window is resized
  1073. setStyleName("v-has-height", height != null && !height.isEmpty());
  1074. }
  1075. private void onDragEvent(Event event) {
  1076. if (!WidgetUtil.isTouchEventOrLeftMouseButton(event)) {
  1077. return;
  1078. }
  1079. switch (DOM.eventGetType(event)) {
  1080. case Event.ONTOUCHSTART:
  1081. if (event.getTouches().length() > 1) {
  1082. return;
  1083. }
  1084. case Event.ONMOUSEDOWN:
  1085. if (!isActive()) {
  1086. bringToFront();
  1087. }
  1088. beginMovingWindow(event);
  1089. break;
  1090. case Event.ONMOUSEUP:
  1091. case Event.ONTOUCHEND:
  1092. case Event.ONTOUCHCANCEL:
  1093. case Event.ONLOSECAPTURE:
  1094. stopMovingWindow();
  1095. break;
  1096. case Event.ONMOUSEMOVE:
  1097. case Event.ONTOUCHMOVE:
  1098. moveWindow(event);
  1099. break;
  1100. default:
  1101. break;
  1102. }
  1103. }
  1104. private void moveWindow(Event event) {
  1105. if (dragging) {
  1106. centered = false;
  1107. if (cursorInsideBrowserContentArea(event)) {
  1108. // Only drag while cursor is inside the browser client area
  1109. final int x = WidgetUtil.getTouchOrMouseClientX(event) - startX
  1110. + origX;
  1111. final int y = WidgetUtil.getTouchOrMouseClientY(event) - startY
  1112. + origY;
  1113. setPopupPosition(x, y);
  1114. }
  1115. DOM.eventPreventDefault(event);
  1116. }
  1117. }
  1118. private void beginMovingWindow(Event event) {
  1119. if (draggable) {
  1120. showDraggingCurtain();
  1121. dragging = true;
  1122. startX = WidgetUtil.getTouchOrMouseClientX(event);
  1123. startY = WidgetUtil.getTouchOrMouseClientY(event);
  1124. origX = DOM.getAbsoluteLeft(getElement());
  1125. origY = DOM.getAbsoluteTop(getElement());
  1126. DOM.setCapture(getElement());
  1127. DOM.eventPreventDefault(event);
  1128. }
  1129. }
  1130. private void stopMovingWindow() {
  1131. dragging = false;
  1132. hideDraggingCurtain();
  1133. DOM.releaseCapture(getElement());
  1134. // fire move event
  1135. fireEvent(new WindowMoveEvent(uidlPositionX, uidlPositionY));
  1136. }
  1137. @Override
  1138. public boolean onEventPreview(Event event) {
  1139. if (dragging) {
  1140. onDragEvent(event);
  1141. return false;
  1142. } else if (resizing) {
  1143. onResizeEvent(event);
  1144. return false;
  1145. }
  1146. // TODO This is probably completely unnecessary as the modality curtain
  1147. // prevents events from reaching other windows and any security check
  1148. // must be done on the server side and not here.
  1149. // The code here is also run many times as each VWindow has an event
  1150. // preview but we cannot check only the current VWindow here (e.g.
  1151. // if(isTopMost) {...}) because PopupPanel will cause all events that
  1152. // are not cancelled here and target this window to be consume():d
  1153. // meaning the event won't be sent to the rest of the preview handlers.
  1154. if (getTopmostWindow() != null && getTopmostWindow().vaadinModality) {
  1155. // Topmost window is modal. Cancel the event if it targets something
  1156. // outside that window (except debug console...)
  1157. if (DOM.getCaptureElement() != null) {
  1158. // Allow events when capture is set
  1159. return true;
  1160. }
  1161. final Element target = event.getEventTarget().cast();
  1162. if (!DOM.isOrHasChild(getTopmostWindow().getElement(), target)) {
  1163. // not within the modal window, but let's see if it's in the
  1164. // debug window
  1165. Widget w = WidgetUtil.findWidget(target);
  1166. while (w != null) {
  1167. if (w instanceof VDebugWindow) {
  1168. return true; // allow debug-window clicks
  1169. } else if (ConnectorMap.get(client).isConnector(w)) {
  1170. return false;
  1171. }
  1172. w = w.getParent();
  1173. }
  1174. return false;
  1175. }
  1176. }
  1177. return true;
  1178. }
  1179. @Override
  1180. public void addStyleDependentName(String styleSuffix) {
  1181. // VWindow's getStyleElement() does not return the same element as
  1182. // getElement(), so we need to override this.
  1183. setStyleName(getElement(), getStylePrimaryName() + "-" + styleSuffix,
  1184. true);
  1185. }
  1186. @Override
  1187. public ShortcutActionHandler getShortcutActionHandler() {
  1188. return shortcutHandler;
  1189. }
  1190. @Override
  1191. public void onScroll(ScrollEvent event) {
  1192. client.updateVariable(id, "scrollTop", contentPanel.getScrollPosition(),
  1193. false);
  1194. client.updateVariable(id, "scrollLeft",
  1195. contentPanel.getHorizontalScrollPosition(), false);
  1196. }
  1197. @Override
  1198. public void onKeyDown(KeyDownEvent event) {
  1199. if (vaadinModality && event.getNativeKeyCode() == KeyCodes.KEY_BACKSPACE
  1200. && !isFocusedElementEditable()) {
  1201. event.preventDefault();
  1202. }
  1203. if (shortcutHandler != null) {
  1204. shortcutHandler
  1205. .handleKeyboardEvent(Event.as(event.getNativeEvent()));
  1206. return;
  1207. }
  1208. }
  1209. @Override
  1210. public void onBlur(BlurEvent event) {
  1211. if (connector.hasEventListener(EventId.BLUR)) {
  1212. client.updateVariable(id, EventId.BLUR, "", true);
  1213. }
  1214. }
  1215. @Override
  1216. public void onFocus(FocusEvent event) {
  1217. if (connector.hasEventListener(EventId.FOCUS)) {
  1218. client.updateVariable(id, EventId.FOCUS, "", true);
  1219. }
  1220. }
  1221. @Override
  1222. public void focus() {
  1223. // We don't want to use contentPanel.focus() as that will use a timer in
  1224. // Chrome/Safari and ultimately run focus events in the wrong order when
  1225. // opening a modal window and focusing some other component at the same
  1226. // time
  1227. contentPanel.getElement().focus();
  1228. }
  1229. private int getDecorationHeight() {
  1230. LayoutManager lm = getLayoutManager();
  1231. int headerHeight = lm.getOuterHeight(header);
  1232. int footerHeight = lm.getOuterHeight(footer);
  1233. return headerHeight + footerHeight;
  1234. }
  1235. private LayoutManager getLayoutManager() {
  1236. return LayoutManager.get(client);
  1237. }
  1238. private int getDecorationWidth() {
  1239. LayoutManager layoutManager = getLayoutManager();
  1240. return layoutManager.getOuterWidth(getElement())
  1241. - contentPanel.getElement().getOffsetWidth();
  1242. }
  1243. /**
  1244. * Allows to specify which connectors contain the description for the
  1245. * window. Text contained in the widgets of the connectors will be read by
  1246. * assistive devices when it is opened.
  1247. * <p>
  1248. * When the provided array is empty, an existing description is removed.
  1249. *
  1250. * @param connectors
  1251. * with the connectors of the widgets to use as description
  1252. */
  1253. public void setAssistiveDescription(Connector[] connectors) {
  1254. if (connectors != null) {
  1255. assistiveConnectors = connectors;
  1256. if (connectors.length == 0) {
  1257. Roles.getDialogRole()
  1258. .removeAriaDescribedbyProperty(getElement());
  1259. } else {
  1260. Id[] ids = new Id[connectors.length];
  1261. for (int index = 0; index < connectors.length; index++) {
  1262. if (connectors[index] == null) {
  1263. throw new IllegalArgumentException(
  1264. "All values in parameter description need to be non-null");
  1265. }
  1266. Element element = ((ComponentConnector) connectors[index])
  1267. .getWidget().getElement();
  1268. AriaHelper.ensureHasId(element);
  1269. ids[index] = Id.of(element);
  1270. }
  1271. Roles.getDialogRole().setAriaDescribedbyProperty(getElement(),
  1272. ids);
  1273. }
  1274. } else {
  1275. throw new IllegalArgumentException(
  1276. "Parameter description must be non-null");
  1277. }
  1278. }
  1279. /**
  1280. * Gets the connectors that are used as assistive description. Text
  1281. * contained in these connectors will be read by assistive devices when the
  1282. * window is opened.
  1283. *
  1284. * @return list of previously set connectors
  1285. */
  1286. public List<Connector> getAssistiveDescription() {
  1287. return Collections.unmodifiableList(Arrays.asList(assistiveConnectors));
  1288. }
  1289. /**
  1290. * Sets the WAI-ARIA role the window.
  1291. *
  1292. * This role defines how an assistive device handles a window. Available
  1293. * roles are alertdialog and dialog (@see
  1294. * <a href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
  1295. * Model</a>).
  1296. *
  1297. * The default role is dialog.
  1298. *
  1299. * @param role
  1300. * WAI-ARIA role to set for the window
  1301. */
  1302. public void setWaiAriaRole(WindowRole role) {
  1303. if (role == WindowRole.ALERTDIALOG) {
  1304. Roles.getAlertdialogRole().set(getElement());
  1305. } else {
  1306. Roles.getDialogRole().set(getElement());
  1307. }
  1308. }
  1309. /**
  1310. * Registers the handlers that prevent to leave the window using the
  1311. * Tab-key.
  1312. * <p>
  1313. * The value of the parameter doTabStop is stored and used for non-modal
  1314. * windows. For modal windows, the handlers are always registered, while
  1315. * preserving the stored value.
  1316. *
  1317. * @param doTabStop
  1318. * true to prevent leaving the window, false to allow leaving the
  1319. * window for non modal windows
  1320. */
  1321. public void setTabStopEnabled(boolean doTabStop) {
  1322. this.doTabStop = doTabStop;
  1323. if (doTabStop || vaadinModality) {
  1324. addTabBlockHandlers();
  1325. } else {
  1326. removeTabBlockHandlers();
  1327. }
  1328. }
  1329. /**
  1330. * Adds a Handler for when user moves the window.
  1331. *
  1332. * @since 7.1.9
  1333. *
  1334. * @return {@link HandlerRegistration} used to remove the handler
  1335. */
  1336. public HandlerRegistration addMoveHandler(WindowMoveHandler handler) {
  1337. return addHandler(handler, WindowMoveEvent.getType());
  1338. }
  1339. /**
  1340. * Adds a Handler for window order change event.
  1341. *
  1342. * @since 8.0
  1343. *
  1344. * @return registration object to deregister the handler
  1345. */
  1346. public static HandlerRegistration addWindowOrderHandler(
  1347. WindowOrderHandler handler) {
  1348. return WINDOW_ORDER_HANDLER.addHandler(WindowOrderEvent.getType(),
  1349. handler);
  1350. }
  1351. /**
  1352. * Checks if a modal window is currently open.
  1353. *
  1354. * @return <code>true</code> if a modal window is open, <code>false</code>
  1355. * otherwise.
  1356. */
  1357. public static boolean isModalWindowOpen() {
  1358. return Document.get().getBody()
  1359. .hasClassName(MODAL_WINDOW_OPEN_CLASSNAME);
  1360. }
  1361. }