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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  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 java.util.List;
  18. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  19. import com.google.gwt.dom.client.Element;
  20. import com.google.gwt.event.dom.client.HasScrollHandlers;
  21. import com.google.gwt.event.dom.client.ScrollEvent;
  22. import com.google.gwt.event.dom.client.ScrollHandler;
  23. import com.google.gwt.event.logical.shared.HasResizeHandlers;
  24. import com.google.gwt.event.logical.shared.ResizeEvent;
  25. import com.google.gwt.event.logical.shared.ResizeHandler;
  26. import com.google.gwt.event.shared.HandlerRegistration;
  27. import com.google.gwt.user.client.Timer;
  28. import com.google.gwt.user.client.Window;
  29. import com.google.gwt.user.client.ui.SimplePanel;
  30. import com.vaadin.client.ApplicationConnection;
  31. import com.vaadin.client.ComponentConnector;
  32. import com.vaadin.client.ConnectorMap;
  33. import com.vaadin.client.Focusable;
  34. import com.vaadin.client.LayoutManager;
  35. import com.vaadin.client.Profiler;
  36. import com.vaadin.client.VConsole;
  37. import com.vaadin.client.WidgetUtil;
  38. import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
  39. import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
  40. import com.vaadin.client.ui.ui.UIConnector;
  41. import com.vaadin.shared.ApplicationConstants;
  42. /**
  43. *
  44. */
  45. public class VUI extends SimplePanel implements ResizeHandler,
  46. Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable,
  47. com.google.gwt.user.client.ui.Focusable, HasResizeHandlers,
  48. HasScrollHandlers {
  49. private static int MONITOR_PARENT_TIMER_INTERVAL = 1000;
  50. /** For internal use only. May be removed or replaced in the future. */
  51. public String id;
  52. /** For internal use only. May be removed or replaced in the future. */
  53. public ShortcutActionHandler actionHandler;
  54. /*
  55. * Last known window size used to detect whether VView should be layouted
  56. * again. Detection must check window size, because the VView size might be
  57. * fixed and thus not automatically adapt to changed window sizes.
  58. */
  59. private int windowWidth;
  60. private int windowHeight;
  61. /*
  62. * Last know view size used to detect whether new dimensions should be sent
  63. * to the server.
  64. */
  65. private int viewWidth;
  66. private int viewHeight;
  67. /** For internal use only. May be removed or replaced in the future. */
  68. public ApplicationConnection connection;
  69. /**
  70. * Keep track of possible parent size changes when an embedded application.
  71. *
  72. * Uses {@link #parentWidth} and {@link #parentHeight} as an optimization to
  73. * keep track of when there is a real change.
  74. */
  75. private Timer resizeTimer;
  76. /** stored width of parent for embedded application auto-resize */
  77. private int parentWidth;
  78. /** stored height of parent for embedded application auto-resize */
  79. private int parentHeight;
  80. /** For internal use only. May be removed or replaced in the future. */
  81. public boolean resizeLazy = false;
  82. private TouchScrollHandler touchScrollHandler;
  83. private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
  84. new ScheduledCommand() {
  85. @Override
  86. public void execute() {
  87. performSizeCheck();
  88. }
  89. });
  90. private Element storedFocus;
  91. public VUI() {
  92. super();
  93. // Allow focusing the view by using the focus() method, the view
  94. // should not be in the document focus flow
  95. getElement().setTabIndex(-1);
  96. makeScrollable();
  97. }
  98. /**
  99. * Start to periodically monitor for parent element resizes if embedded
  100. * application (e.g. portlet).
  101. */
  102. @Override
  103. protected void onLoad() {
  104. super.onLoad();
  105. if (isMonitoringParentSize()) {
  106. resizeTimer = new Timer() {
  107. @Override
  108. public void run() {
  109. // trigger check to see if parent size has changed,
  110. // recalculate layouts
  111. performSizeCheck();
  112. resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
  113. }
  114. };
  115. resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
  116. }
  117. }
  118. /**
  119. * Stop monitoring for parent element resizes.
  120. */
  121. @Override
  122. protected void onUnload() {
  123. if (resizeTimer != null) {
  124. resizeTimer.cancel();
  125. resizeTimer = null;
  126. }
  127. super.onUnload();
  128. }
  129. /**
  130. * Called when the window or parent div might have been resized.
  131. *
  132. * This immediately checks the sizes of the window and the parent div (if
  133. * monitoring it) and triggers layout recalculation if they have changed.
  134. */
  135. protected void performSizeCheck() {
  136. windowSizeMaybeChanged(Window.getClientWidth(),
  137. Window.getClientHeight());
  138. }
  139. /**
  140. * Called when the window or parent div might have been resized.
  141. *
  142. * This immediately checks the sizes of the window and the parent div (if
  143. * monitoring it) and triggers layout recalculation if they have changed.
  144. *
  145. * @param newWindowWidth
  146. * The new width of the window
  147. * @param newWindowHeight
  148. * The new height of the window
  149. *
  150. * @deprecated use {@link #performSizeCheck()}
  151. */
  152. @Deprecated
  153. protected void windowSizeMaybeChanged(int newWindowWidth,
  154. int newWindowHeight) {
  155. if (connection == null) {
  156. // Connection is null if the timer fires before the first UIDL
  157. // update
  158. return;
  159. }
  160. boolean changed = false;
  161. ComponentConnector connector = ConnectorMap.get(connection)
  162. .getConnector(this);
  163. if (windowWidth != newWindowWidth) {
  164. windowWidth = newWindowWidth;
  165. changed = true;
  166. connector.getLayoutManager().reportOuterWidth(connector,
  167. newWindowWidth);
  168. VConsole.log("New window width: " + windowWidth);
  169. }
  170. if (windowHeight != newWindowHeight) {
  171. windowHeight = newWindowHeight;
  172. changed = true;
  173. connector.getLayoutManager().reportOuterHeight(connector,
  174. newWindowHeight);
  175. VConsole.log("New window height: " + windowHeight);
  176. }
  177. Element parentElement = getElement().getParentElement();
  178. if (isMonitoringParentSize() && parentElement != null) {
  179. // check also for parent size changes
  180. int newParentWidth = parentElement.getClientWidth();
  181. int newParentHeight = parentElement.getClientHeight();
  182. if (parentWidth != newParentWidth) {
  183. parentWidth = newParentWidth;
  184. changed = true;
  185. VConsole.log("New parent width: " + parentWidth);
  186. }
  187. if (parentHeight != newParentHeight) {
  188. parentHeight = newParentHeight;
  189. changed = true;
  190. VConsole.log("New parent height: " + parentHeight);
  191. }
  192. }
  193. if (changed) {
  194. /*
  195. * If the window size has changed, layout the VView again and send
  196. * new size to the server if the size changed. (Just checking VView
  197. * size would cause us to ignore cases when a relatively sized VView
  198. * should shrink as the content's size is fixed and would thus not
  199. * automatically shrink.)
  200. */
  201. VConsole.log(
  202. "Running layout functions due to window or parent resize");
  203. // update size to avoid (most) redundant re-layout passes
  204. // there can still be an extra layout recalculation if webkit
  205. // overflow fix updates the size in a deferred block
  206. if (isMonitoringParentSize() && parentElement != null) {
  207. parentWidth = parentElement.getClientWidth();
  208. parentHeight = parentElement.getClientHeight();
  209. }
  210. sendClientResized();
  211. LayoutManager layoutManager = connector.getLayoutManager();
  212. if (layoutManager.isLayoutRunning()) {
  213. layoutManager.layoutLater();
  214. } else {
  215. layoutManager.layoutNow();
  216. }
  217. }
  218. }
  219. /**
  220. * @return the name of the theme in use by this UI.
  221. * @deprecated as of 7.3. Use {@link UIConnector#getActiveTheme()} instead.
  222. */
  223. @Deprecated
  224. public String getTheme() {
  225. return ((UIConnector) ConnectorMap.get(connection).getConnector(this))
  226. .getActiveTheme();
  227. }
  228. /**
  229. * Returns true if the body is NOT generated, i.e if someone else has made
  230. * the page that we're running in. Otherwise we're in charge of the whole
  231. * page.
  232. *
  233. * @return true if we're running embedded
  234. */
  235. public boolean isEmbedded() {
  236. return !getElement().getOwnerDocument().getBody().getClassName()
  237. .contains(ApplicationConstants.GENERATED_BODY_CLASSNAME);
  238. }
  239. /**
  240. * Returns true if the size of the parent should be checked periodically and
  241. * the application should react to its changes.
  242. *
  243. * @return true if size of parent should be tracked
  244. */
  245. protected boolean isMonitoringParentSize() {
  246. // could also perform a more specific check (Liferay portlet)
  247. return isEmbedded();
  248. }
  249. /*
  250. * (non-Javadoc)
  251. *
  252. * @see
  253. * com.google.gwt.event.logical.shared.ResizeHandler#onResize(com.google
  254. * .gwt.event.logical.shared.ResizeEvent)
  255. */
  256. @Override
  257. public void onResize(ResizeEvent event) {
  258. triggerSizeChangeCheck();
  259. }
  260. /**
  261. * Called when a resize event is received.
  262. *
  263. * This may trigger a lazy refresh or perform the size check immediately
  264. * depending on the browser used and whether the server side requests
  265. * resizes to be lazy.
  266. */
  267. private void triggerSizeChangeCheck() {
  268. /*
  269. * We may postpone these events to avoid slowness when resizing the
  270. * browser window. Constantly recalculating the layout causes the resize
  271. * operation to be really slow with complex layouts.
  272. */
  273. boolean lazy = resizeLazy;
  274. if (lazy) {
  275. delayedResizeExecutor.trigger();
  276. } else {
  277. performSizeCheck();
  278. }
  279. }
  280. /**
  281. * Send new dimensions to the server.
  282. * <p>
  283. * For internal use only. May be removed or replaced in the future.
  284. */
  285. public void sendClientResized() {
  286. Profiler.enter("VUI.sendClientResized");
  287. Element parentElement = getElement().getParentElement();
  288. int viewHeight = parentElement.getClientHeight();
  289. int viewWidth = parentElement.getClientWidth();
  290. ResizeEvent.fire(this, viewWidth, viewHeight);
  291. Profiler.leave("VUI.sendClientResized");
  292. }
  293. public static native void goTo(String url)
  294. /*-{
  295. $wnd.location = url;
  296. }-*/;
  297. @Override
  298. public void onWindowClosing(Window.ClosingEvent event) {
  299. // Ensure that any change in the currently focused component is noted
  300. // before refreshing. Ensures that e.g. text in the focused text field
  301. // does not disappear on refresh (when preserve on refresh is enabled)
  302. connection.flushActiveConnector();
  303. }
  304. private static native void loadAppIdListFromDOM(List<String> list)
  305. /*-{
  306. for (var j in $wnd.vaadin.vaadinConfigurations) {
  307. // $entry not needed as function is not exported
  308. list.@java.util.Collection::add(Ljava/lang/Object;)(j);
  309. }
  310. }-*/;
  311. @Override
  312. public ShortcutActionHandler getShortcutActionHandler() {
  313. return actionHandler;
  314. }
  315. @Override
  316. public void focus() {
  317. setFocus(true);
  318. }
  319. /**
  320. * Ensures the widget is scrollable eg. after style name changes.
  321. * <p>
  322. * For internal use only. May be removed or replaced in the future.
  323. */
  324. public void makeScrollable() {
  325. if (touchScrollHandler == null) {
  326. touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
  327. }
  328. touchScrollHandler.addElement(getElement());
  329. }
  330. @Override
  331. public HandlerRegistration addResizeHandler(ResizeHandler resizeHandler) {
  332. return addHandler(resizeHandler, ResizeEvent.getType());
  333. }
  334. @Override
  335. public HandlerRegistration addScrollHandler(ScrollHandler scrollHandler) {
  336. return addHandler(scrollHandler, ScrollEvent.getType());
  337. }
  338. @Override
  339. public int getTabIndex() {
  340. return FocusUtil.getTabIndex(this);
  341. }
  342. @Override
  343. public void setAccessKey(char key) {
  344. FocusUtil.setAccessKey(this, key);
  345. }
  346. @Override
  347. public void setFocus(boolean focused) {
  348. FocusUtil.setFocus(this, focused);
  349. }
  350. @Override
  351. public void setTabIndex(int index) {
  352. FocusUtil.setTabIndex(this, index);
  353. }
  354. /**
  355. * Allows to store the currently focused Element.
  356. *
  357. * Current use case is to store the focus when a Window is opened. Does
  358. * currently handle only a single value. Needs to be extended for #12158
  359. *
  360. * @param focusedElement
  361. */
  362. public void storeFocus() {
  363. storedFocus = WidgetUtil.getFocusedElement();
  364. }
  365. /**
  366. * Restores the previously stored focus Element.
  367. *
  368. * Current use case is to restore the focus when a Window is closed. Does
  369. * currently handle only a single value. Needs to be extended for #12158
  370. */
  371. public void focusStoredElement() {
  372. if (storedFocus != null) {
  373. storedFocus.focus();
  374. }
  375. }
  376. }