123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- /*
- * Copyright 2000-2021 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
- package com.vaadin.client.ui;
-
- import java.util.List;
- import java.util.logging.Logger;
-
- import com.google.gwt.dom.client.Element;
- import com.google.gwt.event.dom.client.HasScrollHandlers;
- import com.google.gwt.event.dom.client.ScrollEvent;
- import com.google.gwt.event.dom.client.ScrollHandler;
- import com.google.gwt.event.logical.shared.HasResizeHandlers;
- import com.google.gwt.event.logical.shared.ResizeEvent;
- import com.google.gwt.event.logical.shared.ResizeHandler;
- import com.google.gwt.event.shared.HandlerRegistration;
- import com.google.gwt.user.client.Timer;
- import com.google.gwt.user.client.Window;
- import com.google.gwt.user.client.ui.SimplePanel;
- import com.vaadin.client.ApplicationConnection;
- import com.vaadin.client.ComponentConnector;
- import com.vaadin.client.ConnectorMap;
- import com.vaadin.client.Focusable;
- import com.vaadin.client.LayoutManager;
- import com.vaadin.client.Profiler;
- import com.vaadin.client.WidgetUtil;
- import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
- import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
- import com.vaadin.client.ui.ui.UIConnector;
- import com.vaadin.shared.ApplicationConstants;
-
- /**
- * Widget class for the UI.
- *
- * @author Vaadin Ltd
- *
- */
- public class VUI extends SimplePanel implements ResizeHandler,
- Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable,
- com.google.gwt.user.client.ui.Focusable, HasResizeHandlers,
- HasScrollHandlers {
-
- private static final int MONITOR_PARENT_TIMER_INTERVAL = 1000;
-
- /** For internal use only. May be removed or replaced in the future. */
- public String id;
-
- /** For internal use only. May be removed or replaced in the future. */
- public ShortcutActionHandler actionHandler;
-
- /*
- * Last known window size used to detect whether VView should be layouted
- * again. Detection must check window size, because the VView size might be
- * fixed and thus not automatically adapt to changed window sizes.
- */
- private int windowWidth;
- private int windowHeight;
-
- /*
- * Last know view size used to detect whether new dimensions should be sent
- * to the server.
- */
- private int viewWidth;
- private int viewHeight;
-
- /** For internal use only. May be removed or replaced in the future. */
- public ApplicationConnection connection;
-
- /**
- * Keep track of possible parent size changes when an embedded application.
- *
- * Uses {@link #parentWidth} and {@link #parentHeight} as an optimization to
- * keep track of when there is a real change.
- */
- private Timer resizeTimer;
-
- /** stored width of parent for embedded application auto-resize */
- private int parentWidth;
-
- /** stored height of parent for embedded application auto-resize */
- private int parentHeight;
-
- /** For internal use only. May be removed or replaced in the future. */
- public boolean resizeLazy = false;
-
- private TouchScrollHandler touchScrollHandler;
-
- private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
- () -> performSizeCheck());
-
- private Element storedFocus;
-
- /**
- * Constructs a widget for an UI.
- */
- public VUI() {
- super();
- // Allow focusing the view by using the focus() method, the view
- // should not be in the document focus flow
- getElement().setTabIndex(-1);
- makeScrollable();
- }
-
- /**
- * Start to periodically monitor for parent element resizes if embedded
- * application (e.g. portlet).
- */
- @Override
- protected void onLoad() {
- super.onLoad();
- if (isMonitoringParentSize()) {
- resizeTimer = new Timer() {
-
- @Override
- public void run() {
- // trigger check to see if parent size has changed,
- // recalculate layouts
- performSizeCheck();
- resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
- }
- };
- resizeTimer.schedule(MONITOR_PARENT_TIMER_INTERVAL);
- }
- }
-
- /**
- * Stop monitoring for parent element resizes.
- */
-
- @Override
- protected void onUnload() {
- if (resizeTimer != null) {
- resizeTimer.cancel();
- resizeTimer = null;
- }
- super.onUnload();
- }
-
- /**
- * Called when the window or parent div might have been resized.
- *
- * This immediately checks the sizes of the window and the parent div (if
- * monitoring it) and triggers layout recalculation if they have changed.
- */
- protected void performSizeCheck() {
- windowSizeMaybeChanged(Window.getClientWidth(),
- Window.getClientHeight());
- }
-
- /**
- * Called when the window or parent div might have been resized.
- *
- * This immediately checks the sizes of the window and the parent div (if
- * monitoring it) and triggers layout recalculation if they have changed.
- *
- * @param newWindowWidth
- * The new width of the window
- * @param newWindowHeight
- * The new height of the window
- *
- * @deprecated use {@link #performSizeCheck()}
- */
- @Deprecated
- protected void windowSizeMaybeChanged(int newWindowWidth,
- int newWindowHeight) {
- if (connection == null) {
- // Connection is null if the timer fires before the first UIDL
- // update
- return;
- }
-
- boolean changed = false;
- ComponentConnector connector = ConnectorMap.get(connection)
- .getConnector(this);
- if (windowWidth != newWindowWidth) {
- windowWidth = newWindowWidth;
- changed = true;
- connector.getLayoutManager().reportOuterWidth(connector,
- newWindowWidth);
- getLogger().info("New window width: " + windowWidth);
- }
- if (windowHeight != newWindowHeight) {
- windowHeight = newWindowHeight;
- changed = true;
- connector.getLayoutManager().reportOuterHeight(connector,
- newWindowHeight);
- getLogger().info("New window height: " + windowHeight);
- }
- Element parentElement = getElement().getParentElement();
- if (isMonitoringParentSize() && parentElement != null) {
- // check also for parent size changes
- int newParentWidth = parentElement.getClientWidth();
- int newParentHeight = parentElement.getClientHeight();
- if (parentWidth != newParentWidth) {
- parentWidth = newParentWidth;
- changed = true;
- getLogger().info("New parent width: " + parentWidth);
- }
- if (parentHeight != newParentHeight) {
- parentHeight = newParentHeight;
- changed = true;
- getLogger().info("New parent height: " + parentHeight);
- }
- }
- if (changed) {
- /*
- * If the window size has changed, layout the VView again and send
- * new size to the server if the size changed. (Just checking VView
- * size would cause us to ignore cases when a relatively sized VView
- * should shrink as the content's size is fixed and would thus not
- * automatically shrink.)
- */
- getLogger().info(
- "Running layout functions due to window or parent resize");
-
- // update size to avoid (most) redundant re-layout passes
- // there can still be an extra layout recalculation if webkit
- // overflow fix updates the size in a deferred block
- if (isMonitoringParentSize() && parentElement != null) {
- parentWidth = parentElement.getClientWidth();
- parentHeight = parentElement.getClientHeight();
- }
-
- sendClientResized();
-
- LayoutManager layoutManager = connector.getLayoutManager();
- if (layoutManager.isLayoutRunning()) {
- layoutManager.layoutLater();
- } else {
- layoutManager.layoutNow();
- }
- }
- }
-
- /**
- * @return the name of the theme in use by this UI.
- * @deprecated as of 7.3. Use {@link UIConnector#getActiveTheme()} instead.
- */
- @Deprecated
- public String getTheme() {
- return ((UIConnector) ConnectorMap.get(connection).getConnector(this))
- .getActiveTheme();
- }
-
- /**
- * Returns true if the body is NOT generated, i.e if someone else has made
- * the page that we're running in. Otherwise we're in charge of the whole
- * page.
- *
- * @return true if we're running embedded
- */
- public boolean isEmbedded() {
- return !getElement().getOwnerDocument().getBody().getClassName()
- .contains(ApplicationConstants.GENERATED_BODY_CLASSNAME);
- }
-
- /**
- * Returns true if the size of the parent should be checked periodically and
- * the application should react to its changes.
- *
- * @return true if size of parent should be tracked
- */
- protected boolean isMonitoringParentSize() {
- // could also perform a more specific check (Liferay portlet)
- return isEmbedded();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.google.gwt.event.logical.shared.ResizeHandler#onResize(com.google
- * .gwt.event.logical.shared.ResizeEvent)
- */
-
- @Override
- public void onResize(ResizeEvent event) {
- triggerSizeChangeCheck();
- }
-
- /**
- * Called when a resize event is received.
- *
- * This may trigger a lazy refresh or perform the size check immediately
- * depending on the browser used and whether the server side requests
- * resizes to be lazy.
- */
- private void triggerSizeChangeCheck() {
- /*
- * We may postpone these events to avoid slowness when resizing the
- * browser window. Constantly recalculating the layout causes the resize
- * operation to be really slow with complex layouts.
- */
- boolean lazy = resizeLazy;
-
- if (lazy) {
- delayedResizeExecutor.trigger();
- } else {
- performSizeCheck();
- }
- }
-
- /**
- * Send new dimensions to the server.
- * <p>
- * For internal use only. May be removed or replaced in the future.
- */
- public void sendClientResized() {
- Profiler.enter("VUI.sendClientResized");
- Element parentElement = getElement().getParentElement();
- int viewHeight = parentElement.getClientHeight();
- int viewWidth = parentElement.getClientWidth();
-
- ResizeEvent.fire(this, viewWidth, viewHeight);
- Profiler.leave("VUI.sendClientResized");
- }
-
- /**
- *
- * Opens the given URL in the current browser window. If this UI needs to be
- * closed as a result it should be handled separately.
- *
- * @param url
- * the URL to navigate to
- */
- public static native void goTo(String url)
- /*-{
- $wnd.location = url;
- }-*/;
-
- @Override
- public void onWindowClosing(Window.ClosingEvent event) {
- // Ensure that any change in the currently focused component is noted
- // before refreshing. Ensures that e.g. text in the focused text field
- // does not disappear on refresh (when preserve on refresh is enabled)
- connection.flushActiveConnector();
- }
-
- private static native void loadAppIdListFromDOM(List<String> list)
- /*-{
- for (var j in $wnd.vaadin.vaadinConfigurations) {
- // $entry not needed as function is not exported
- list.@java.util.Collection::add(Ljava/lang/Object;)(j);
- }
- }-*/;
-
- @Override
- public ShortcutActionHandler getShortcutActionHandler() {
- return actionHandler;
- }
-
- @Override
- public void focus() {
- setFocus(true);
- }
-
- /**
- * Ensures the widget is scrollable e.g. after style name changes.
- * <p>
- * For internal use only. May be removed or replaced in the future.
- */
- public void makeScrollable() {
- if (touchScrollHandler == null) {
- touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this);
- }
- touchScrollHandler.addElement(getElement());
- }
-
- @Override
- public HandlerRegistration addResizeHandler(ResizeHandler resizeHandler) {
- return addHandler(resizeHandler, ResizeEvent.getType());
- }
-
- @Override
- public HandlerRegistration addScrollHandler(ScrollHandler scrollHandler) {
- return addHandler(scrollHandler, ScrollEvent.getType());
- }
-
- @Override
- public int getTabIndex() {
- return FocusUtil.getTabIndex(this);
- }
-
- @Override
- public void setAccessKey(char key) {
- FocusUtil.setAccessKey(this, key);
- }
-
- @Override
- public void setFocus(boolean focused) {
- FocusUtil.setFocus(this, focused);
- }
-
- @Override
- public void setTabIndex(int index) {
- FocusUtil.setTabIndex(this, index);
- }
-
- /**
- * Allows to store the currently focused Element.
- *
- * Current use case is to store the focus when a Window is opened. Does
- * currently handle only a single value. Needs to be extended for #12158
- *
- * @param focusedElement
- */
- public void storeFocus() {
- storedFocus = WidgetUtil.getFocusedElement();
- }
-
- /**
- * Restores the previously stored focus Element.
- *
- * Current use case is to restore the focus when a Window is closed. Does
- * currently handle only a single value. Needs to be extended for #12158
- */
- public void focusStoredElement() {
- if (storedFocus != null) {
- storedFocus.focus();
- }
- }
-
- private static Logger getLogger() {
- return Logger.getLogger(VUI.class.getName());
- }
- }
|