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

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