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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. /*
  2. * Copyright 2000-2014 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.ArrayList;
  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.logical.shared.ValueChangeEvent;
  27. import com.google.gwt.event.logical.shared.ValueChangeHandler;
  28. import com.google.gwt.event.shared.HandlerRegistration;
  29. import com.google.gwt.http.client.URL;
  30. import com.google.gwt.user.client.History;
  31. import com.google.gwt.user.client.Timer;
  32. import com.google.gwt.user.client.Window;
  33. import com.google.gwt.user.client.ui.SimplePanel;
  34. import com.vaadin.client.ApplicationConnection;
  35. import com.vaadin.client.BrowserInfo;
  36. import com.vaadin.client.ComponentConnector;
  37. import com.vaadin.client.ConnectorMap;
  38. import com.vaadin.client.Focusable;
  39. import com.vaadin.client.LayoutManager;
  40. import com.vaadin.client.Profiler;
  41. import com.vaadin.client.VConsole;
  42. import com.vaadin.client.WidgetUtil;
  43. import com.vaadin.client.legacy.ui.VLegacyTextField;
  44. import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
  45. import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
  46. import com.vaadin.client.ui.ui.UIConnector;
  47. import com.vaadin.shared.ApplicationConstants;
  48. import com.vaadin.shared.ui.ui.UIConstants;
  49. /**
  50. *
  51. */
  52. public class VUI extends SimplePanel implements ResizeHandler,
  53. Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable,
  54. com.google.gwt.user.client.ui.Focusable, HasResizeHandlers,
  55. HasScrollHandlers {
  56. private static int MONITOR_PARENT_TIMER_INTERVAL = 1000;
  57. /** For internal use only. May be removed or replaced in the future. */
  58. public String id;
  59. /** For internal use only. May be removed or replaced in the future. */
  60. public ShortcutActionHandler actionHandler;
  61. /*
  62. * Last known window size used to detect whether VView should be layouted
  63. * again. Detection must check window size, because the VView size might be
  64. * fixed and thus not automatically adapt to changed window sizes.
  65. */
  66. private int windowWidth;
  67. private int windowHeight;
  68. /*
  69. * Last know view size used to detect whether new dimensions should be sent
  70. * to the server.
  71. */
  72. private int viewWidth;
  73. private int viewHeight;
  74. /** For internal use only. May be removed or replaced in the future. */
  75. public ApplicationConnection connection;
  76. /**
  77. * Keep track of possible parent size changes when an embedded application.
  78. *
  79. * Uses {@link #parentWidth} and {@link #parentHeight} as an optimization to
  80. * keep track of when there is a real change.
  81. */
  82. private Timer resizeTimer;
  83. /** stored width of parent for embedded application auto-resize */
  84. private int parentWidth;
  85. /** stored height of parent for embedded application auto-resize */
  86. private int parentHeight;
  87. /** For internal use only. May be removed or replaced in the future. */
  88. public boolean immediate;
  89. /** For internal use only. May be removed or replaced in the future. */
  90. public boolean resizeLazy = false;
  91. private HandlerRegistration historyHandlerRegistration;
  92. private TouchScrollHandler touchScrollHandler;
  93. /**
  94. * The current URI fragment, used to avoid sending updates if nothing has
  95. * changed.
  96. * <p>
  97. * For internal use only. May be removed or replaced in the future.
  98. */
  99. public String currentFragment;
  100. /**
  101. * Listener for URI fragment changes. Notifies the server of the new value
  102. * whenever the value changes.
  103. */
  104. private final ValueChangeHandler<String> historyChangeHandler = new ValueChangeHandler<String>() {
  105. @Override
  106. public void onValueChange(ValueChangeEvent<String> event) {
  107. String newFragment = event.getValue();
  108. // Send the location to the server if the fragment has changed
  109. // and flush active connectors in UI.
  110. if (!newFragment.equals(currentFragment) && connection != null) {
  111. /*
  112. * Ensure the fragment is properly encoded in all browsers
  113. * (#10769)
  114. *
  115. * createUrlBuilder does not properly pass an empty fragment to
  116. * UrlBuilder on Webkit browsers so do it manually (#11686)
  117. */
  118. String location = Window.Location
  119. .createUrlBuilder()
  120. .setHash(
  121. URL.decodeQueryString(Window.Location.getHash()))
  122. .buildString();
  123. currentFragment = newFragment;
  124. connection.flushActiveConnector();
  125. connection.updateVariable(id, UIConstants.LOCATION_VARIABLE,
  126. location, true);
  127. }
  128. }
  129. };
  130. private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
  131. new ScheduledCommand() {
  132. @Override
  133. public void execute() {
  134. performSizeCheck();
  135. }
  136. });
  137. private Element storedFocus;
  138. public VUI() {
  139. super();
  140. // Allow focusing the view by using the focus() method, the view
  141. // should not be in the document focus flow
  142. getElement().setTabIndex(-1);
  143. makeScrollable();
  144. }
  145. /**
  146. * Start to periodically monitor for parent element resizes if embedded
  147. * application (e.g. portlet).
  148. */
  149. @Override
  150. protected void onLoad() {
  151. super.onLoad();
  152. if (isMonitoringParentSize()) {
  153. resizeTimer = new Timer() {
  154. @Override
  155. public void run() {
  156. // trigger check to see if parent size has changed,
  157. // recalculate layouts
  158. performSizeCheck();
  159. resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
  160. }
  161. };
  162. resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
  163. }
  164. }
  165. @Override
  166. protected void onAttach() {
  167. super.onAttach();
  168. historyHandlerRegistration = History
  169. .addValueChangeHandler(historyChangeHandler);
  170. currentFragment = History.getToken();
  171. }
  172. @Override
  173. protected void onDetach() {
  174. super.onDetach();
  175. historyHandlerRegistration.removeHandler();
  176. historyHandlerRegistration = null;
  177. }
  178. /**
  179. * Stop monitoring for parent element resizes.
  180. */
  181. @Override
  182. protected void onUnload() {
  183. if (resizeTimer != null) {
  184. resizeTimer.cancel();
  185. resizeTimer = null;
  186. }
  187. super.onUnload();
  188. }
  189. /**
  190. * Called when the window or parent div might have been resized.
  191. *
  192. * This immediately checks the sizes of the window and the parent div (if
  193. * monitoring it) and triggers layout recalculation if they have changed.
  194. */
  195. protected void performSizeCheck() {
  196. windowSizeMaybeChanged(Window.getClientWidth(),
  197. Window.getClientHeight());
  198. }
  199. /**
  200. * Called when the window or parent div might have been resized.
  201. *
  202. * This immediately checks the sizes of the window and the parent div (if
  203. * monitoring it) and triggers layout recalculation if they have changed.
  204. *
  205. * @param newWindowWidth
  206. * The new width of the window
  207. * @param newWindowHeight
  208. * The new height of the window
  209. *
  210. * @deprecated use {@link #performSizeCheck()}
  211. */
  212. @Deprecated
  213. protected void windowSizeMaybeChanged(int newWindowWidth,
  214. int newWindowHeight) {
  215. if (connection == null) {
  216. // Connection is null if the timer fires before the first UIDL
  217. // update
  218. return;
  219. }
  220. boolean changed = false;
  221. ComponentConnector connector = ConnectorMap.get(connection)
  222. .getConnector(this);
  223. if (windowWidth != newWindowWidth) {
  224. windowWidth = newWindowWidth;
  225. changed = true;
  226. connector.getLayoutManager().reportOuterWidth(connector,
  227. newWindowWidth);
  228. VConsole.log("New window width: " + windowWidth);
  229. }
  230. if (windowHeight != newWindowHeight) {
  231. windowHeight = newWindowHeight;
  232. changed = true;
  233. connector.getLayoutManager().reportOuterHeight(connector,
  234. newWindowHeight);
  235. VConsole.log("New window height: " + windowHeight);
  236. }
  237. Element parentElement = getElement().getParentElement();
  238. if (isMonitoringParentSize() && parentElement != null) {
  239. // check also for parent size changes
  240. int newParentWidth = parentElement.getClientWidth();
  241. int newParentHeight = parentElement.getClientHeight();
  242. if (parentWidth != newParentWidth) {
  243. parentWidth = newParentWidth;
  244. changed = true;
  245. VConsole.log("New parent width: " + parentWidth);
  246. }
  247. if (parentHeight != newParentHeight) {
  248. parentHeight = newParentHeight;
  249. changed = true;
  250. VConsole.log("New parent height: " + parentHeight);
  251. }
  252. }
  253. if (changed) {
  254. /*
  255. * If the window size has changed, layout the VView again and send
  256. * new size to the server if the size changed. (Just checking VView
  257. * size would cause us to ignore cases when a relatively sized VView
  258. * should shrink as the content's size is fixed and would thus not
  259. * automatically shrink.)
  260. */
  261. VConsole.log("Running layout functions due to window or parent resize");
  262. // update size to avoid (most) redundant re-layout passes
  263. // there can still be an extra layout recalculation if webkit
  264. // overflow fix updates the size in a deferred block
  265. if (isMonitoringParentSize() && parentElement != null) {
  266. parentWidth = parentElement.getClientWidth();
  267. parentHeight = parentElement.getClientHeight();
  268. }
  269. sendClientResized();
  270. LayoutManager layoutManager = connector.getLayoutManager();
  271. if (layoutManager.isLayoutRunning()) {
  272. layoutManager.layoutLater();
  273. } else {
  274. layoutManager.layoutNow();
  275. }
  276. }
  277. }
  278. /**
  279. * @return the name of the theme in use by this UI.
  280. * @deprecated as of 7.3. Use {@link UIConnector#getActiveTheme()} instead.
  281. */
  282. @Deprecated
  283. public String getTheme() {
  284. return ((UIConnector) ConnectorMap.get(connection).getConnector(this))
  285. .getActiveTheme();
  286. }
  287. /**
  288. * Returns true if the body is NOT generated, i.e if someone else has made
  289. * the page that we're running in. Otherwise we're in charge of the whole
  290. * page.
  291. *
  292. * @return true if we're running embedded
  293. */
  294. public boolean isEmbedded() {
  295. return !getElement().getOwnerDocument().getBody().getClassName()
  296. .contains(ApplicationConstants.GENERATED_BODY_CLASSNAME);
  297. }
  298. /**
  299. * Returns true if the size of the parent should be checked periodically and
  300. * the application should react to its changes.
  301. *
  302. * @return true if size of parent should be tracked
  303. */
  304. protected boolean isMonitoringParentSize() {
  305. // could also perform a more specific check (Liferay portlet)
  306. return isEmbedded();
  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. * <p>
  347. * For internal use only. May be removed or replaced in the future.
  348. */
  349. public void sendClientResized() {
  350. Profiler.enter("VUI.sendClientResized");
  351. Element parentElement = getElement().getParentElement();
  352. int viewHeight = parentElement.getClientHeight();
  353. int viewWidth = parentElement.getClientWidth();
  354. ResizeEvent.fire(this, viewWidth, viewHeight);
  355. Profiler.leave("VUI.sendClientResized");
  356. }
  357. public native static void goTo(String url)
  358. /*-{
  359. $wnd.location = url;
  360. }-*/;
  361. @Override
  362. public void onWindowClosing(Window.ClosingEvent event) {
  363. // Change focus on this window in order to ensure that all state is
  364. // collected from textfields
  365. // TODO this is a naive hack, that only works with text fields and may
  366. // cause some odd issues. Should be replaced with a decent solution, see
  367. // also related BeforeShortcutActionListener interface. Same interface
  368. // might be usable here.
  369. VLegacyTextField.flushChangesFromFocusedTextField();
  370. }
  371. private native static void loadAppIdListFromDOM(ArrayList<String> list)
  372. /*-{
  373. var j;
  374. for(j in $wnd.vaadin.vaadinConfigurations) {
  375. // $entry not needed as function is not exported
  376. list.@java.util.Collection::add(Ljava/lang/Object;)(j);
  377. }
  378. }-*/;
  379. @Override
  380. public ShortcutActionHandler getShortcutActionHandler() {
  381. return actionHandler;
  382. }
  383. @Override
  384. public void focus() {
  385. setFocus(true);
  386. }
  387. /**
  388. * Ensures the widget is scrollable eg. after style name changes.
  389. * <p>
  390. * For internal use only. May be removed or replaced in the future.
  391. */
  392. public void makeScrollable() {
  393. if (touchScrollHandler == null) {
  394. touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
  395. }
  396. touchScrollHandler.addElement(getElement());
  397. }
  398. @Override
  399. public HandlerRegistration addResizeHandler(ResizeHandler resizeHandler) {
  400. return addHandler(resizeHandler, ResizeEvent.getType());
  401. }
  402. @Override
  403. public HandlerRegistration addScrollHandler(ScrollHandler scrollHandler) {
  404. return addHandler(scrollHandler, ScrollEvent.getType());
  405. }
  406. @Override
  407. public int getTabIndex() {
  408. return FocusUtil.getTabIndex(this);
  409. }
  410. @Override
  411. public void setAccessKey(char key) {
  412. FocusUtil.setAccessKey(this, key);
  413. }
  414. @Override
  415. public void setFocus(boolean focused) {
  416. FocusUtil.setFocus(this, focused);
  417. }
  418. @Override
  419. public void setTabIndex(int index) {
  420. FocusUtil.setTabIndex(this, index);
  421. }
  422. /**
  423. * Allows to store the currently focused Element.
  424. *
  425. * Current use case is to store the focus when a Window is opened. Does
  426. * currently handle only a single value. Needs to be extended for #12158
  427. *
  428. * @param focusedElement
  429. */
  430. public void storeFocus() {
  431. storedFocus = WidgetUtil.getFocusedElement();
  432. }
  433. /**
  434. * Restores the previously stored focus Element.
  435. *
  436. * Current use case is to restore the focus when a Window is closed. Does
  437. * currently handle only a single value. Needs to be extended for #12158
  438. *
  439. * @return the lastFocusElementBeforeDialogOpened
  440. */
  441. public void focusStoredElement() {
  442. if (storedFocus != null) {
  443. storedFocus.focus();
  444. }
  445. }
  446. }