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

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