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 48KB

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