Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

VUI.java 16KB

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