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.

VUI.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. * Copyright 2011 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.terminal.gwt.client.ui.UI;
  17. import java.util.ArrayList;
  18. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  19. import com.google.gwt.dom.client.Element;
  20. import com.google.gwt.event.logical.shared.HasResizeHandlers;
  21. import com.google.gwt.event.logical.shared.ResizeEvent;
  22. import com.google.gwt.event.logical.shared.ResizeHandler;
  23. import com.google.gwt.event.logical.shared.ValueChangeEvent;
  24. import com.google.gwt.event.logical.shared.ValueChangeHandler;
  25. import com.google.gwt.event.shared.HandlerRegistration;
  26. import com.google.gwt.user.client.DOM;
  27. import com.google.gwt.user.client.Event;
  28. import com.google.gwt.user.client.History;
  29. import com.google.gwt.user.client.Timer;
  30. import com.google.gwt.user.client.Window;
  31. import com.google.gwt.user.client.ui.SimplePanel;
  32. import com.vaadin.shared.ApplicationConstants;
  33. import com.vaadin.shared.ui.ui.UIConstants;
  34. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  35. import com.vaadin.terminal.gwt.client.BrowserInfo;
  36. import com.vaadin.terminal.gwt.client.ComponentConnector;
  37. import com.vaadin.terminal.gwt.client.ConnectorMap;
  38. import com.vaadin.terminal.gwt.client.Focusable;
  39. import com.vaadin.terminal.gwt.client.VConsole;
  40. import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler;
  41. import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
  42. import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate;
  43. import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate.TouchScrollHandler;
  44. import com.vaadin.terminal.gwt.client.ui.VLazyExecutor;
  45. import com.vaadin.terminal.gwt.client.ui.textfield.VTextField;
  46. /**
  47. *
  48. */
  49. public class VUI extends SimplePanel implements ResizeHandler,
  50. Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable,
  51. HasResizeHandlers {
  52. private static final String CLASSNAME = "v-view";
  53. private static int MONITOR_PARENT_TIMER_INTERVAL = 1000;
  54. String theme;
  55. String id;
  56. ShortcutActionHandler actionHandler;
  57. /*
  58. * Last known window size used to detect whether VView should be layouted
  59. * again. Detection must check window size, because the VView size might be
  60. * fixed and thus not automatically adapt to changed window sizes.
  61. */
  62. private int windowWidth;
  63. private int windowHeight;
  64. /*
  65. * Last know view size used to detect whether new dimensions should be sent
  66. * to the server.
  67. */
  68. private int viewWidth;
  69. private int viewHeight;
  70. ApplicationConnection connection;
  71. /**
  72. * Keep track of possible parent size changes when an embedded application.
  73. *
  74. * Uses {@link #parentWidth} and {@link #parentHeight} as an optimization to
  75. * keep track of when there is a real change.
  76. */
  77. private Timer resizeTimer;
  78. /** stored width of parent for embedded application auto-resize */
  79. private int parentWidth;
  80. /** stored height of parent for embedded application auto-resize */
  81. private int parentHeight;
  82. int scrollTop;
  83. int scrollLeft;
  84. boolean rendering;
  85. boolean scrollable;
  86. boolean immediate;
  87. boolean resizeLazy = false;
  88. private HandlerRegistration historyHandlerRegistration;
  89. private TouchScrollHandler touchScrollHandler;
  90. /**
  91. * The current URI fragment, used to avoid sending updates if nothing has
  92. * changed.
  93. */
  94. String currentFragment;
  95. /**
  96. * Listener for URI fragment changes. Notifies the server of the new value
  97. * whenever the value changes.
  98. */
  99. private final ValueChangeHandler<String> historyChangeHandler = new ValueChangeHandler<String>() {
  100. @Override
  101. public void onValueChange(ValueChangeEvent<String> event) {
  102. String newFragment = event.getValue();
  103. // Send the new fragment to the server if it has changed
  104. if (!newFragment.equals(currentFragment) && connection != null) {
  105. currentFragment = newFragment;
  106. connection.updateVariable(id, UIConstants.FRAGMENT_VARIABLE,
  107. newFragment, true);
  108. }
  109. }
  110. };
  111. private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
  112. new ScheduledCommand() {
  113. @Override
  114. public void execute() {
  115. performSizeCheck();
  116. }
  117. });
  118. public VUI() {
  119. super();
  120. setStyleName(CLASSNAME);
  121. // Allow focusing the view by using the focus() method, the view
  122. // should not be in the document focus flow
  123. getElement().setTabIndex(-1);
  124. makeScrollable();
  125. }
  126. /**
  127. * Start to periodically monitor for parent element resizes if embedded
  128. * application (e.g. portlet).
  129. */
  130. @Override
  131. protected void onLoad() {
  132. super.onLoad();
  133. if (isMonitoringParentSize()) {
  134. resizeTimer = new Timer() {
  135. @Override
  136. public void run() {
  137. // trigger check to see if parent size has changed,
  138. // recalculate layouts
  139. performSizeCheck();
  140. resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
  141. }
  142. };
  143. resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
  144. }
  145. }
  146. @Override
  147. protected void onAttach() {
  148. super.onAttach();
  149. historyHandlerRegistration = History
  150. .addValueChangeHandler(historyChangeHandler);
  151. currentFragment = History.getToken();
  152. }
  153. @Override
  154. protected void onDetach() {
  155. super.onDetach();
  156. historyHandlerRegistration.removeHandler();
  157. historyHandlerRegistration = null;
  158. }
  159. /**
  160. * Stop monitoring for parent element resizes.
  161. */
  162. @Override
  163. protected void onUnload() {
  164. if (resizeTimer != null) {
  165. resizeTimer.cancel();
  166. resizeTimer = null;
  167. }
  168. super.onUnload();
  169. }
  170. /**
  171. * Called when the window or parent div might have been resized.
  172. *
  173. * This immediately checks the sizes of the window and the parent div (if
  174. * monitoring it) and triggers layout recalculation if they have changed.
  175. */
  176. protected void performSizeCheck() {
  177. windowSizeMaybeChanged(Window.getClientWidth(),
  178. Window.getClientHeight());
  179. }
  180. /**
  181. * Called when the window or parent div might have been resized.
  182. *
  183. * This immediately checks the sizes of the window and the parent div (if
  184. * monitoring it) and triggers layout recalculation if they have changed.
  185. *
  186. * @param newWindowWidth
  187. * The new width of the window
  188. * @param newWindowHeight
  189. * The new height of the window
  190. *
  191. * @deprecated use {@link #performSizeCheck()}
  192. */
  193. @Deprecated
  194. protected void windowSizeMaybeChanged(int newWindowWidth,
  195. int newWindowHeight) {
  196. boolean changed = false;
  197. ComponentConnector connector = ConnectorMap.get(connection)
  198. .getConnector(this);
  199. if (windowWidth != newWindowWidth) {
  200. windowWidth = newWindowWidth;
  201. changed = true;
  202. connector.getLayoutManager().reportOuterWidth(connector,
  203. newWindowWidth);
  204. VConsole.log("New window width: " + windowWidth);
  205. }
  206. if (windowHeight != newWindowHeight) {
  207. windowHeight = newWindowHeight;
  208. changed = true;
  209. connector.getLayoutManager().reportOuterHeight(connector,
  210. newWindowHeight);
  211. VConsole.log("New window height: " + windowHeight);
  212. }
  213. Element parentElement = getElement().getParentElement();
  214. if (isMonitoringParentSize() && parentElement != null) {
  215. // check also for parent size changes
  216. int newParentWidth = parentElement.getClientWidth();
  217. int newParentHeight = parentElement.getClientHeight();
  218. if (parentWidth != newParentWidth) {
  219. parentWidth = newParentWidth;
  220. changed = true;
  221. VConsole.log("New parent width: " + parentWidth);
  222. }
  223. if (parentHeight != newParentHeight) {
  224. parentHeight = newParentHeight;
  225. changed = true;
  226. VConsole.log("New parent height: " + parentHeight);
  227. }
  228. }
  229. if (changed) {
  230. /*
  231. * If the window size has changed, layout the VView again and send
  232. * new size to the server if the size changed. (Just checking VView
  233. * size would cause us to ignore cases when a relatively sized VView
  234. * should shrink as the content's size is fixed and would thus not
  235. * automatically shrink.)
  236. */
  237. VConsole.log("Running layout functions due to window or parent resize");
  238. // update size to avoid (most) redundant re-layout passes
  239. // there can still be an extra layout recalculation if webkit
  240. // overflow fix updates the size in a deferred block
  241. if (isMonitoringParentSize() && parentElement != null) {
  242. parentWidth = parentElement.getClientWidth();
  243. parentHeight = parentElement.getClientHeight();
  244. }
  245. sendClientResized();
  246. connector.getLayoutManager().layoutNow();
  247. }
  248. }
  249. public String getTheme() {
  250. return theme;
  251. }
  252. /**
  253. * Used to reload host page on theme changes.
  254. */
  255. static native void reloadHostPage()
  256. /*-{
  257. $wnd.location.reload();
  258. }-*/;
  259. /**
  260. * Returns true if the body is NOT generated, i.e if someone else has made
  261. * the page that we're running in. Otherwise we're in charge of the whole
  262. * page.
  263. *
  264. * @return true if we're running embedded
  265. */
  266. public boolean isEmbedded() {
  267. return !getElement().getOwnerDocument().getBody().getClassName()
  268. .contains(ApplicationConstants.GENERATED_BODY_CLASSNAME);
  269. }
  270. /**
  271. * Returns true if the size of the parent should be checked periodically and
  272. * the application should react to its changes.
  273. *
  274. * @return true if size of parent should be tracked
  275. */
  276. protected boolean isMonitoringParentSize() {
  277. // could also perform a more specific check (Liferay portlet)
  278. return isEmbedded();
  279. }
  280. @Override
  281. public void onBrowserEvent(Event event) {
  282. super.onBrowserEvent(event);
  283. int type = DOM.eventGetType(event);
  284. if (type == Event.ONKEYDOWN && actionHandler != null) {
  285. actionHandler.handleKeyboardEvent(event);
  286. return;
  287. } else if (scrollable && type == Event.ONSCROLL) {
  288. updateScrollPosition();
  289. }
  290. }
  291. /**
  292. * Updates scroll position from DOM and saves variables to server.
  293. */
  294. private void updateScrollPosition() {
  295. int oldTop = scrollTop;
  296. int oldLeft = scrollLeft;
  297. scrollTop = DOM.getElementPropertyInt(getElement(), "scrollTop");
  298. scrollLeft = DOM.getElementPropertyInt(getElement(), "scrollLeft");
  299. if (connection != null && !rendering) {
  300. if (oldTop != scrollTop) {
  301. connection.updateVariable(id, "scrollTop", scrollTop, false);
  302. }
  303. if (oldLeft != scrollLeft) {
  304. connection.updateVariable(id, "scrollLeft", scrollLeft, false);
  305. }
  306. }
  307. }
  308. /*
  309. * (non-Javadoc)
  310. *
  311. * @see
  312. * com.google.gwt.event.logical.shared.ResizeHandler#onResize(com.google
  313. * .gwt.event.logical.shared.ResizeEvent)
  314. */
  315. @Override
  316. public void onResize(ResizeEvent event) {
  317. triggerSizeChangeCheck();
  318. }
  319. /**
  320. * Called when a resize event is received.
  321. *
  322. * This may trigger a lazy refresh or perform the size check immediately
  323. * depending on the browser used and whether the server side requests
  324. * resizes to be lazy.
  325. */
  326. private void triggerSizeChangeCheck() {
  327. /*
  328. * IE (pre IE9 at least) will give us some false resize events due to
  329. * problems with scrollbars. Firefox 3 might also produce some extra
  330. * events. We postpone both the re-layouting and the server side event
  331. * for a while to deal with these issues.
  332. *
  333. * We may also postpone these events to avoid slowness when resizing the
  334. * browser window. Constantly recalculating the layout causes the resize
  335. * operation to be really slow with complex layouts.
  336. */
  337. boolean lazy = resizeLazy || BrowserInfo.get().isIE8();
  338. if (lazy) {
  339. delayedResizeExecutor.trigger();
  340. } else {
  341. performSizeCheck();
  342. }
  343. }
  344. /**
  345. * Send new dimensions to the server.
  346. */
  347. void sendClientResized() {
  348. Element parentElement = getElement().getParentElement();
  349. int viewHeight = parentElement.getClientHeight();
  350. int viewWidth = parentElement.getClientWidth();
  351. ResizeEvent.fire(this, viewWidth, viewHeight);
  352. }
  353. public native static void goTo(String url)
  354. /*-{
  355. $wnd.location = url;
  356. }-*/;
  357. @Override
  358. public void onWindowClosing(Window.ClosingEvent event) {
  359. // Change focus on this window in order to ensure that all state is
  360. // collected from textfields
  361. // TODO this is a naive hack, that only works with text fields and may
  362. // cause some odd issues. Should be replaced with a decent solution, see
  363. // also related BeforeShortcutActionListener interface. Same interface
  364. // might be usable here.
  365. VTextField.flushChangesFromFocusedTextField();
  366. }
  367. private native static void loadAppIdListFromDOM(ArrayList<String> list)
  368. /*-{
  369. var j;
  370. for(j in $wnd.vaadin.vaadinConfigurations) {
  371. // $entry not needed as function is not exported
  372. list.@java.util.Collection::add(Ljava/lang/Object;)(j);
  373. }
  374. }-*/;
  375. @Override
  376. public ShortcutActionHandler getShortcutActionHandler() {
  377. return actionHandler;
  378. }
  379. @Override
  380. public void focus() {
  381. getElement().focus();
  382. }
  383. /**
  384. * Ensures the root is scrollable eg. after style name changes.
  385. */
  386. void makeScrollable() {
  387. if (touchScrollHandler == null) {
  388. touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
  389. }
  390. touchScrollHandler.addElement(getElement());
  391. }
  392. @Override
  393. public HandlerRegistration addResizeHandler(ResizeHandler resizeHandler) {
  394. return addHandler(resizeHandler, ResizeEvent.getType());
  395. }
  396. }