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

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