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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import java.util.ArrayList;
  6. import java.util.Arrays;
  7. import java.util.Comparator;
  8. import com.google.gwt.core.client.Scheduler;
  9. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  10. import com.google.gwt.event.dom.client.BlurEvent;
  11. import com.google.gwt.event.dom.client.BlurHandler;
  12. import com.google.gwt.event.dom.client.FocusEvent;
  13. import com.google.gwt.event.dom.client.FocusHandler;
  14. import com.google.gwt.event.dom.client.KeyDownEvent;
  15. import com.google.gwt.event.dom.client.KeyDownHandler;
  16. import com.google.gwt.event.dom.client.ScrollEvent;
  17. import com.google.gwt.event.dom.client.ScrollHandler;
  18. import com.google.gwt.user.client.Command;
  19. import com.google.gwt.user.client.DOM;
  20. import com.google.gwt.user.client.Element;
  21. import com.google.gwt.user.client.Event;
  22. import com.google.gwt.user.client.Window;
  23. import com.google.gwt.user.client.ui.HasWidgets;
  24. import com.google.gwt.user.client.ui.RootPanel;
  25. import com.google.gwt.user.client.ui.Widget;
  26. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  27. import com.vaadin.terminal.gwt.client.BrowserInfo;
  28. import com.vaadin.terminal.gwt.client.ComponentConnector;
  29. import com.vaadin.terminal.gwt.client.ConnectorMap;
  30. import com.vaadin.terminal.gwt.client.Console;
  31. import com.vaadin.terminal.gwt.client.EventId;
  32. import com.vaadin.terminal.gwt.client.Focusable;
  33. import com.vaadin.terminal.gwt.client.LayoutManager;
  34. import com.vaadin.terminal.gwt.client.Util;
  35. import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
  36. /**
  37. * "Sub window" component.
  38. *
  39. * @author Vaadin Ltd
  40. */
  41. public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
  42. ScrollHandler, KeyDownHandler, FocusHandler, BlurHandler, Focusable {
  43. /**
  44. * Minimum allowed height of a window. This refers to the content area, not
  45. * the outer borders.
  46. */
  47. private static final int MIN_CONTENT_AREA_HEIGHT = 100;
  48. /**
  49. * Minimum allowed width of a window. This refers to the content area, not
  50. * the outer borders.
  51. */
  52. private static final int MIN_CONTENT_AREA_WIDTH = 150;
  53. private static ArrayList<VWindow> windowOrder = new ArrayList<VWindow>();
  54. private static boolean orderingDefered;
  55. public static final String CLASSNAME = "v-window";
  56. private static final int STACKING_OFFSET_PIXELS = 15;
  57. public static final int Z_INDEX = 10000;
  58. ComponentConnector layout;
  59. Element contents;
  60. Element header;
  61. Element footer;
  62. private Element resizeBox;
  63. final FocusableScrollPanel contentPanel = new FocusableScrollPanel();
  64. private boolean dragging;
  65. private int startX;
  66. private int startY;
  67. private int origX;
  68. private int origY;
  69. private boolean resizing;
  70. private int origW;
  71. private int origH;
  72. Element closeBox;
  73. protected ApplicationConnection client;
  74. String id;
  75. ShortcutActionHandler shortcutHandler;
  76. /** Last known positionx read from UIDL or updated to application connection */
  77. private int uidlPositionX = -1;
  78. /** Last known positiony read from UIDL or updated to application connection */
  79. private int uidlPositionY = -1;
  80. boolean vaadinModality = false;
  81. boolean resizable = true;
  82. private boolean draggable = true;
  83. boolean resizeLazy = false;
  84. private Element modalityCurtain;
  85. private Element draggingCurtain;
  86. private Element resizingCurtain;
  87. private Element headerText;
  88. private boolean closable = true;
  89. // If centered (via UIDL), the window should stay in the centered -mode
  90. // until a position is received from the server, or the user moves or
  91. // resizes the window.
  92. boolean centered = false;
  93. boolean immediate;
  94. private Element wrapper;
  95. boolean visibilityChangesDisabled;
  96. int bringToFrontSequence = -1;
  97. private VLazyExecutor delayedContentsSizeUpdater = new VLazyExecutor(200,
  98. new ScheduledCommand() {
  99. public void execute() {
  100. updateContentsSize();
  101. }
  102. });
  103. public VWindow() {
  104. super(false, false, true); // no autohide, not modal, shadow
  105. // Different style of shadow for windows
  106. setShadowStyle("window");
  107. constructDOM();
  108. contentPanel.addScrollHandler(this);
  109. contentPanel.addKeyDownHandler(this);
  110. contentPanel.addFocusHandler(this);
  111. contentPanel.addBlurHandler(this);
  112. }
  113. public void bringToFront() {
  114. int curIndex = windowOrder.indexOf(this);
  115. if (curIndex + 1 < windowOrder.size()) {
  116. windowOrder.remove(this);
  117. windowOrder.add(this);
  118. for (; curIndex < windowOrder.size(); curIndex++) {
  119. windowOrder.get(curIndex).setWindowOrder(curIndex);
  120. }
  121. }
  122. }
  123. /**
  124. * Returns true if this window is the topmost VWindow
  125. *
  126. * @return
  127. */
  128. private boolean isActive() {
  129. return equals(getTopmostWindow());
  130. }
  131. private static VWindow getTopmostWindow() {
  132. return windowOrder.get(windowOrder.size() - 1);
  133. }
  134. void setWindowOrderAndPosition() {
  135. // This cannot be done in the constructor as the widgets are created in
  136. // a different order than on they should appear on screen
  137. if (windowOrder.contains(this)) {
  138. // Already set
  139. return;
  140. }
  141. final int order = windowOrder.size();
  142. setWindowOrder(order);
  143. windowOrder.add(this);
  144. setPopupPosition(order * STACKING_OFFSET_PIXELS, order
  145. * STACKING_OFFSET_PIXELS);
  146. }
  147. private void setWindowOrder(int order) {
  148. setZIndex(order + Z_INDEX);
  149. }
  150. @Override
  151. protected void setZIndex(int zIndex) {
  152. super.setZIndex(zIndex);
  153. if (vaadinModality) {
  154. DOM.setStyleAttribute(getModalityCurtain(), "zIndex", "" + zIndex);
  155. }
  156. }
  157. protected Element getModalityCurtain() {
  158. if (modalityCurtain == null) {
  159. modalityCurtain = DOM.createDiv();
  160. modalityCurtain.setClassName(CLASSNAME + "-modalitycurtain");
  161. }
  162. return modalityCurtain;
  163. }
  164. protected void constructDOM() {
  165. setStyleName(CLASSNAME);
  166. header = DOM.createDiv();
  167. DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader");
  168. headerText = DOM.createDiv();
  169. DOM.setElementProperty(headerText, "className", CLASSNAME + "-header");
  170. contents = DOM.createDiv();
  171. DOM.setElementProperty(contents, "className", CLASSNAME + "-contents");
  172. footer = DOM.createDiv();
  173. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
  174. resizeBox = DOM.createDiv();
  175. DOM.setElementProperty(resizeBox, "className", CLASSNAME + "-resizebox");
  176. closeBox = DOM.createDiv();
  177. DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox");
  178. DOM.appendChild(footer, resizeBox);
  179. wrapper = DOM.createDiv();
  180. DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap");
  181. DOM.appendChild(wrapper, header);
  182. DOM.appendChild(wrapper, closeBox);
  183. DOM.appendChild(header, headerText);
  184. DOM.appendChild(wrapper, contents);
  185. DOM.appendChild(wrapper, footer);
  186. DOM.appendChild(super.getContainerElement(), wrapper);
  187. sinkEvents(Event.MOUSEEVENTS | Event.TOUCHEVENTS | Event.ONCLICK
  188. | Event.ONLOSECAPTURE);
  189. setWidget(contentPanel);
  190. }
  191. /**
  192. * Calling this method will defer ordering algorithm, to order windows based
  193. * on servers bringToFront and modality instructions. Non changed windows
  194. * will be left intact.
  195. */
  196. static void deferOrdering() {
  197. if (!orderingDefered) {
  198. orderingDefered = true;
  199. Scheduler.get().scheduleFinally(new Command() {
  200. public void execute() {
  201. doServerSideOrdering();
  202. }
  203. });
  204. }
  205. }
  206. private static void doServerSideOrdering() {
  207. orderingDefered = false;
  208. VWindow[] array = windowOrder.toArray(new VWindow[windowOrder.size()]);
  209. Arrays.sort(array, new Comparator<VWindow>() {
  210. public int compare(VWindow o1, VWindow o2) {
  211. /*
  212. * Order by modality, then by bringtofront sequence.
  213. */
  214. if (o1.vaadinModality && !o2.vaadinModality) {
  215. return 1;
  216. } else if (!o1.vaadinModality && o2.vaadinModality) {
  217. return -1;
  218. } else if (o1.bringToFrontSequence > o2.bringToFrontSequence) {
  219. return 1;
  220. } else if (o1.bringToFrontSequence < o2.bringToFrontSequence) {
  221. return -1;
  222. } else {
  223. return 0;
  224. }
  225. }
  226. });
  227. for (int i = 0; i < array.length; i++) {
  228. VWindow w = array[i];
  229. if (w.bringToFrontSequence != -1 || w.vaadinModality) {
  230. w.bringToFront();
  231. w.bringToFrontSequence = -1;
  232. }
  233. }
  234. }
  235. @Override
  236. public void setVisible(boolean visible) {
  237. /*
  238. * Visibility with VWindow works differently than with other Paintables
  239. * in Vaadin. Invisible VWindows are not attached to DOM at all. Flag is
  240. * used to avoid visibility call from
  241. * ApplicationConnection.updateComponent();
  242. */
  243. if (!visibilityChangesDisabled) {
  244. super.setVisible(visible);
  245. }
  246. }
  247. void setDraggable(boolean draggable) {
  248. if (this.draggable == draggable) {
  249. return;
  250. }
  251. this.draggable = draggable;
  252. setCursorProperties();
  253. }
  254. private void setCursorProperties() {
  255. if (!draggable) {
  256. header.getStyle().setProperty("cursor", "default");
  257. footer.getStyle().setProperty("cursor", "default");
  258. } else {
  259. header.getStyle().setProperty("cursor", "");
  260. footer.getStyle().setProperty("cursor", "");
  261. }
  262. }
  263. /**
  264. * Sets the closable state of the window. Additionally hides/shows the close
  265. * button according to the new state.
  266. *
  267. * @param closable
  268. * true if the window can be closed by the user
  269. */
  270. protected void setClosable(boolean closable) {
  271. if (this.closable == closable) {
  272. return;
  273. }
  274. this.closable = closable;
  275. if (closable) {
  276. DOM.setStyleAttribute(closeBox, "display", "");
  277. } else {
  278. DOM.setStyleAttribute(closeBox, "display", "none");
  279. }
  280. }
  281. /**
  282. * Returns the closable state of the sub window. If the sub window is
  283. * closable a decoration (typically an X) is shown to the user. By clicking
  284. * on the X the user can close the window.
  285. *
  286. * @return true if the sub window is closable
  287. */
  288. protected boolean isClosable() {
  289. return closable;
  290. }
  291. @Override
  292. public void show() {
  293. if (!windowOrder.contains(this)) {
  294. windowOrder.add(this);
  295. }
  296. if (vaadinModality) {
  297. showModalityCurtain();
  298. }
  299. super.show();
  300. }
  301. @Override
  302. public void hide() {
  303. if (vaadinModality) {
  304. hideModalityCurtain();
  305. }
  306. super.hide();
  307. windowOrder.remove(this);
  308. }
  309. void setVaadinModality(boolean modality) {
  310. vaadinModality = modality;
  311. if (vaadinModality) {
  312. if (isAttached()) {
  313. showModalityCurtain();
  314. }
  315. deferOrdering();
  316. } else {
  317. if (modalityCurtain != null) {
  318. if (isAttached()) {
  319. hideModalityCurtain();
  320. }
  321. modalityCurtain = null;
  322. }
  323. }
  324. }
  325. private void showModalityCurtain() {
  326. DOM.setStyleAttribute(getModalityCurtain(), "zIndex",
  327. "" + (windowOrder.indexOf(this) + Z_INDEX));
  328. if (isShowing()) {
  329. RootPanel.getBodyElement().insertBefore(getModalityCurtain(),
  330. getElement());
  331. } else {
  332. DOM.appendChild(RootPanel.getBodyElement(), getModalityCurtain());
  333. }
  334. }
  335. private void hideModalityCurtain() {
  336. DOM.removeChild(RootPanel.getBodyElement(), modalityCurtain);
  337. }
  338. /*
  339. * Shows an empty div on top of all other content; used when moving, so that
  340. * iframes (etc) do not steal event.
  341. */
  342. private void showDraggingCurtain() {
  343. DOM.appendChild(RootPanel.getBodyElement(), getDraggingCurtain());
  344. }
  345. private void hideDraggingCurtain() {
  346. if (draggingCurtain != null) {
  347. DOM.removeChild(RootPanel.getBodyElement(), draggingCurtain);
  348. }
  349. }
  350. /*
  351. * Shows an empty div on top of all other content; used when resizing, so
  352. * that iframes (etc) do not steal event.
  353. */
  354. private void showResizingCurtain() {
  355. DOM.appendChild(RootPanel.getBodyElement(), getResizingCurtain());
  356. }
  357. private void hideResizingCurtain() {
  358. if (resizingCurtain != null) {
  359. DOM.removeChild(RootPanel.getBodyElement(), resizingCurtain);
  360. }
  361. }
  362. private Element getDraggingCurtain() {
  363. if (draggingCurtain == null) {
  364. draggingCurtain = createCurtain();
  365. draggingCurtain.setClassName(CLASSNAME + "-draggingCurtain");
  366. }
  367. return draggingCurtain;
  368. }
  369. private Element getResizingCurtain() {
  370. if (resizingCurtain == null) {
  371. resizingCurtain = createCurtain();
  372. resizingCurtain.setClassName(CLASSNAME + "-resizingCurtain");
  373. }
  374. return resizingCurtain;
  375. }
  376. private Element createCurtain() {
  377. Element curtain = DOM.createDiv();
  378. DOM.setStyleAttribute(curtain, "position", "absolute");
  379. DOM.setStyleAttribute(curtain, "top", "0px");
  380. DOM.setStyleAttribute(curtain, "left", "0px");
  381. DOM.setStyleAttribute(curtain, "width", "100%");
  382. DOM.setStyleAttribute(curtain, "height", "100%");
  383. DOM.setStyleAttribute(curtain, "zIndex", "" + VOverlay.Z_INDEX);
  384. return curtain;
  385. }
  386. void setResizable(boolean resizability) {
  387. resizable = resizability;
  388. if (resizability) {
  389. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer");
  390. DOM.setElementProperty(resizeBox, "className", CLASSNAME
  391. + "-resizebox");
  392. } else {
  393. DOM.setElementProperty(footer, "className", CLASSNAME + "-footer "
  394. + CLASSNAME + "-footer-noresize");
  395. DOM.setElementProperty(resizeBox, "className", CLASSNAME
  396. + "-resizebox " + CLASSNAME + "-resizebox-disabled");
  397. }
  398. }
  399. @Override
  400. public void setPopupPosition(int left, int top) {
  401. if (top < 0) {
  402. // ensure window is not moved out of browser window from top of the
  403. // screen
  404. top = 0;
  405. }
  406. super.setPopupPosition(left, top);
  407. if (left != uidlPositionX && client != null) {
  408. client.updateVariable(id, "positionx", left, false);
  409. uidlPositionX = left;
  410. }
  411. if (top != uidlPositionY && client != null) {
  412. client.updateVariable(id, "positiony", top, false);
  413. uidlPositionY = top;
  414. }
  415. }
  416. public void setCaption(String c) {
  417. setCaption(c, null);
  418. }
  419. public void setCaption(String c, String icon) {
  420. String html = Util.escapeHTML(c);
  421. if (icon != null) {
  422. icon = client.translateVaadinUri(icon);
  423. html = "<img src=\"" + Util.escapeAttribute(icon)
  424. + "\" class=\"v-icon\" />" + html;
  425. }
  426. DOM.setInnerHTML(headerText, html);
  427. }
  428. @Override
  429. protected Element getContainerElement() {
  430. // in GWT 1.5 this method is used in PopupPanel constructor
  431. if (contents == null) {
  432. return super.getContainerElement();
  433. }
  434. return contents;
  435. }
  436. @Override
  437. public void onBrowserEvent(final Event event) {
  438. boolean bubble = true;
  439. final int type = event.getTypeInt();
  440. final Element target = DOM.eventGetTarget(event);
  441. if (client != null && header.isOrHasChild(target)) {
  442. // Handle window caption tooltips
  443. client.handleTooltipEvent(event, this);
  444. }
  445. if (resizing || resizeBox == target) {
  446. onResizeEvent(event);
  447. bubble = false;
  448. } else if (isClosable() && target == closeBox) {
  449. if (type == Event.ONCLICK) {
  450. onCloseClick();
  451. }
  452. bubble = false;
  453. } else if (dragging || !contents.isOrHasChild(target)) {
  454. onDragEvent(event);
  455. bubble = false;
  456. } else if (type == Event.ONCLICK) {
  457. // clicked inside window, ensure to be on top
  458. if (!isActive()) {
  459. bringToFront();
  460. }
  461. }
  462. /*
  463. * If clicking on other than the content, move focus to the window.
  464. * After that this windows e.g. gets all keyboard shortcuts.
  465. */
  466. if (type == Event.ONMOUSEDOWN
  467. && !contentPanel.getElement().isOrHasChild(target)
  468. && target != closeBox) {
  469. contentPanel.focus();
  470. }
  471. if (!bubble) {
  472. event.stopPropagation();
  473. } else {
  474. // Super.onBrowserEvent takes care of Handlers added by the
  475. // ClickEventHandler
  476. super.onBrowserEvent(event);
  477. }
  478. }
  479. private void onCloseClick() {
  480. client.updateVariable(id, "close", true, true);
  481. }
  482. private void onResizeEvent(Event event) {
  483. if (resizable && Util.isTouchEventOrLeftMouseButton(event)) {
  484. switch (event.getTypeInt()) {
  485. case Event.ONMOUSEDOWN:
  486. case Event.ONTOUCHSTART:
  487. if (!isActive()) {
  488. bringToFront();
  489. }
  490. showResizingCurtain();
  491. if (BrowserInfo.get().isIE()) {
  492. DOM.setStyleAttribute(resizeBox, "visibility", "hidden");
  493. }
  494. resizing = true;
  495. startX = Util.getTouchOrMouseClientX(event);
  496. startY = Util.getTouchOrMouseClientY(event);
  497. origW = getElement().getOffsetWidth();
  498. origH = getElement().getOffsetHeight();
  499. DOM.setCapture(getElement());
  500. event.preventDefault();
  501. break;
  502. case Event.ONMOUSEUP:
  503. case Event.ONTOUCHEND:
  504. setSize(event, true);
  505. case Event.ONTOUCHCANCEL:
  506. DOM.releaseCapture(getElement());
  507. case Event.ONLOSECAPTURE:
  508. hideResizingCurtain();
  509. if (BrowserInfo.get().isIE()) {
  510. DOM.setStyleAttribute(resizeBox, "visibility", "");
  511. }
  512. resizing = false;
  513. break;
  514. case Event.ONMOUSEMOVE:
  515. case Event.ONTOUCHMOVE:
  516. if (resizing) {
  517. centered = false;
  518. setSize(event, false);
  519. event.preventDefault();
  520. }
  521. break;
  522. default:
  523. event.preventDefault();
  524. break;
  525. }
  526. }
  527. }
  528. /**
  529. * TODO check if we need to support this with touch based devices.
  530. *
  531. * Checks if the cursor was inside the browser content area when the event
  532. * happened.
  533. *
  534. * @param event
  535. * The event to be checked
  536. * @return true, if the cursor is inside the browser content area
  537. *
  538. * false, otherwise
  539. */
  540. private boolean cursorInsideBrowserContentArea(Event event) {
  541. if (event.getClientX() < 0 || event.getClientY() < 0) {
  542. // Outside to the left or above
  543. return false;
  544. }
  545. if (event.getClientX() > Window.getClientWidth()
  546. || event.getClientY() > Window.getClientHeight()) {
  547. // Outside to the right or below
  548. return false;
  549. }
  550. return true;
  551. }
  552. private void setSize(Event event, boolean updateVariables) {
  553. if (!cursorInsideBrowserContentArea(event)) {
  554. // Only drag while cursor is inside the browser client area
  555. return;
  556. }
  557. int w = Util.getTouchOrMouseClientX(event) - startX + origW;
  558. int minWidth = getMinWidth();
  559. if (w < minWidth) {
  560. w = minWidth;
  561. }
  562. int h = Util.getTouchOrMouseClientY(event) - startY + origH;
  563. int minHeight = getMinHeight();
  564. if (h < minHeight) {
  565. h = minHeight;
  566. }
  567. setWidth(w + "px");
  568. setHeight(h + "px");
  569. if (updateVariables) {
  570. // sending width back always as pixels, no need for unit
  571. client.updateVariable(id, "width", w, false);
  572. client.updateVariable(id, "height", h, immediate);
  573. }
  574. if (updateVariables || !resizeLazy) {
  575. // Resize has finished or is not lazy
  576. updateContentsSize();
  577. } else {
  578. // Lazy resize - wait for a while before re-rendering contents
  579. delayedContentsSizeUpdater.trigger();
  580. }
  581. }
  582. private void updateContentsSize() {
  583. // Update child widget dimensions
  584. if (client != null) {
  585. client.handleComponentRelativeSize(layout.getWidget());
  586. client.runDescendentsLayout((HasWidgets) layout.getWidget());
  587. }
  588. LayoutManager layoutManager = LayoutManager.get(client);
  589. layoutManager.setNeedsMeasure(ConnectorMap.get(client).getConnector(
  590. this));
  591. layoutManager.layoutNow();
  592. }
  593. @Override
  594. public void setWidth(String width) {
  595. // Override PopupPanel which sets the width to the contents
  596. getElement().getStyle().setProperty("width", width);
  597. // Update v-has-width in case undefined window is resized
  598. setStyleName("v-has-width", width != null && width.length() > 0);
  599. }
  600. @Override
  601. public void setHeight(String height) {
  602. // Override PopupPanel which sets the height to the contents
  603. getElement().getStyle().setProperty("height", height);
  604. // Update v-has-height in case undefined window is resized
  605. setStyleName("v-has-height", height != null && height.length() > 0);
  606. }
  607. private void onDragEvent(Event event) {
  608. if (!Util.isTouchEventOrLeftMouseButton(event)) {
  609. return;
  610. }
  611. switch (DOM.eventGetType(event)) {
  612. case Event.ONTOUCHSTART:
  613. if (event.getTouches().length() > 1) {
  614. return;
  615. }
  616. case Event.ONMOUSEDOWN:
  617. if (!isActive()) {
  618. bringToFront();
  619. }
  620. beginMovingWindow(event);
  621. break;
  622. case Event.ONMOUSEUP:
  623. case Event.ONTOUCHEND:
  624. case Event.ONTOUCHCANCEL:
  625. case Event.ONLOSECAPTURE:
  626. stopMovingWindow();
  627. break;
  628. case Event.ONMOUSEMOVE:
  629. case Event.ONTOUCHMOVE:
  630. moveWindow(event);
  631. break;
  632. default:
  633. break;
  634. }
  635. }
  636. private void moveWindow(Event event) {
  637. if (dragging) {
  638. centered = false;
  639. if (cursorInsideBrowserContentArea(event)) {
  640. // Only drag while cursor is inside the browser client area
  641. final int x = Util.getTouchOrMouseClientX(event) - startX
  642. + origX;
  643. final int y = Util.getTouchOrMouseClientY(event) - startY
  644. + origY;
  645. setPopupPosition(x, y);
  646. }
  647. DOM.eventPreventDefault(event);
  648. }
  649. }
  650. private void beginMovingWindow(Event event) {
  651. if (draggable) {
  652. showDraggingCurtain();
  653. dragging = true;
  654. startX = Util.getTouchOrMouseClientX(event);
  655. startY = Util.getTouchOrMouseClientY(event);
  656. origX = DOM.getAbsoluteLeft(getElement());
  657. origY = DOM.getAbsoluteTop(getElement());
  658. DOM.setCapture(getElement());
  659. DOM.eventPreventDefault(event);
  660. }
  661. }
  662. private void stopMovingWindow() {
  663. dragging = false;
  664. hideDraggingCurtain();
  665. DOM.releaseCapture(getElement());
  666. }
  667. @Override
  668. public boolean onEventPreview(Event event) {
  669. if (dragging) {
  670. onDragEvent(event);
  671. return false;
  672. } else if (resizing) {
  673. onResizeEvent(event);
  674. return false;
  675. }
  676. // TODO This is probably completely unnecessary as the modality curtain
  677. // prevents events from reaching other windows and any security check
  678. // must be done on the server side and not here.
  679. // The code here is also run many times as each VWindow has an event
  680. // preview but we cannot check only the current VWindow here (e.g.
  681. // if(isTopMost) {...}) because PopupPanel will cause all events that
  682. // are not cancelled here and target this window to be consume():d
  683. // meaning the event won't be sent to the rest of the preview handlers.
  684. if (getTopmostWindow().vaadinModality) {
  685. // Topmost window is modal. Cancel the event if it targets something
  686. // outside that window (except debug console...)
  687. if (DOM.getCaptureElement() != null) {
  688. // Allow events when capture is set
  689. return true;
  690. }
  691. final Element target = event.getEventTarget().cast();
  692. if (!DOM.isOrHasChild(getTopmostWindow().getElement(), target)) {
  693. // not within the modal window, but let's see if it's in the
  694. // debug window
  695. Widget w = Util.findWidget(target, null);
  696. while (w != null) {
  697. if (w instanceof Console) {
  698. return true; // allow debug-window clicks
  699. } else if (ConnectorMap.get(client).isConnector(w)) {
  700. return false;
  701. }
  702. w = w.getParent();
  703. }
  704. return false;
  705. }
  706. }
  707. return true;
  708. }
  709. @Override
  710. public void addStyleDependentName(String styleSuffix) {
  711. // VWindow's getStyleElement() does not return the same element as
  712. // getElement(), so we need to override this.
  713. setStyleName(getElement(), getStylePrimaryName() + "-" + styleSuffix,
  714. true);
  715. }
  716. public ShortcutActionHandler getShortcutActionHandler() {
  717. return shortcutHandler;
  718. }
  719. public void onScroll(ScrollEvent event) {
  720. client.updateVariable(id, "scrollTop",
  721. contentPanel.getScrollPosition(), false);
  722. client.updateVariable(id, "scrollLeft",
  723. contentPanel.getHorizontalScrollPosition(), false);
  724. }
  725. public void onKeyDown(KeyDownEvent event) {
  726. if (shortcutHandler != null) {
  727. shortcutHandler
  728. .handleKeyboardEvent(Event.as(event.getNativeEvent()));
  729. return;
  730. }
  731. }
  732. public void onBlur(BlurEvent event) {
  733. if (client.hasEventListeners(this, EventId.BLUR)) {
  734. client.updateVariable(id, EventId.BLUR, "", true);
  735. }
  736. }
  737. public void onFocus(FocusEvent event) {
  738. if (client.hasEventListeners(this, EventId.FOCUS)) {
  739. client.updateVariable(id, EventId.FOCUS, "", true);
  740. }
  741. }
  742. public void focus() {
  743. contentPanel.focus();
  744. }
  745. public int getMinHeight() {
  746. return MIN_CONTENT_AREA_HEIGHT + getDecorationHeight();
  747. }
  748. private int getDecorationHeight() {
  749. LayoutManager layoutManager = layout.getLayoutManager();
  750. return layoutManager.getOuterHeight(getElement())
  751. - layoutManager.getInnerHeight(contentPanel.getElement());
  752. }
  753. public int getMinWidth() {
  754. return MIN_CONTENT_AREA_WIDTH + getDecorationWidth();
  755. }
  756. private int getDecorationWidth() {
  757. LayoutManager layoutManager = layout.getLayoutManager();
  758. return layoutManager.getOuterWidth(getElement())
  759. - layoutManager.getInnerWidth(contentPanel.getElement());
  760. }
  761. }