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

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