All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.ui.UI Maven / Gradle / Ivy

There is a newer version: 8.27.3
Show newest version
/*
 * Copyright (C) 2000-2024 Vaadin Ltd
 *
 * This program is available under Vaadin Commercial License and Service Terms.
 *
 * See  for the full
 * license.
 */

package com.vaadin.ui;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.event.Action;
import com.vaadin.event.Action.Handler;
import com.vaadin.event.ActionManager;
import com.vaadin.event.ConnectorEventListener;
import com.vaadin.event.MouseEvents.ClickEvent;
import com.vaadin.event.MouseEvents.ClickListener;
import com.vaadin.event.UIEvents.PollEvent;
import com.vaadin.event.UIEvents.PollListener;
import com.vaadin.event.UIEvents.PollNotifier;
import com.vaadin.navigator.Navigator;
import com.vaadin.navigator.PushStateNavigation;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.ComponentSizeValidator;
import com.vaadin.server.ComponentSizeValidator.InvalidLayout;
import com.vaadin.server.DefaultErrorHandler;
import com.vaadin.server.ErrorHandler;
import com.vaadin.server.ErrorHandlingRunnable;
import com.vaadin.server.LocaleService;
import com.vaadin.server.Page;
import com.vaadin.server.PaintException;
import com.vaadin.server.PaintTarget;
import com.vaadin.server.UIProvider;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinServlet;
import com.vaadin.server.VaadinSession;
import com.vaadin.server.VaadinSession.State;
import com.vaadin.server.communication.AtmospherePushConnection;
import com.vaadin.server.communication.PushConnection;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.Connector;
import com.vaadin.shared.EventId;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.Registration;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.DelayedCallbackRpc;
import com.vaadin.shared.ui.WindowOrderRpc;
import com.vaadin.shared.ui.ui.DebugWindowClientRpc;
import com.vaadin.shared.ui.ui.DebugWindowServerRpc;
import com.vaadin.shared.ui.ui.PageClientRpc;
import com.vaadin.shared.ui.ui.ScrollClientRpc;
import com.vaadin.shared.ui.ui.UIClientRpc;
import com.vaadin.shared.ui.ui.UIConstants;
import com.vaadin.shared.ui.ui.UIServerRpc;
import com.vaadin.shared.ui.ui.UIState;
import com.vaadin.ui.Component.Focusable;
import com.vaadin.ui.Dependency.Type;
import com.vaadin.ui.Window.WindowOrderChangeListener;
import com.vaadin.ui.declarative.Design;
import com.vaadin.ui.dnd.DragSourceExtension;
import com.vaadin.ui.dnd.DropTargetExtension;
import com.vaadin.util.ConnectorHelper;
import com.vaadin.util.CurrentInstance;
import com.vaadin.util.ReflectTools;

/**
 * The topmost component in any component hierarchy. There is one UI for every
 * Vaadin instance in a browser window. A UI may either represent an entire
 * browser window (or tab) or some part of a html page where a Vaadin
 * application is embedded.
 * 

* The UI is the server side entry point for various client side features that * are not represented as components added to a layout, e.g notifications, sub * windows, and executing javascript in the browser. *

*

* When a new UI instance is needed, typically because the user opens a URL in a * browser window which points to e.g. {@link VaadinServlet}, all * {@link UIProvider}s registered to the current {@link VaadinSession} are * queried for the UI class that should be used. The selection is by default * based on the UI init parameter from web.xml. *

*

* After a UI has been created by the application, it is initialized using * {@link #init(VaadinRequest)}. This method is intended to be overridden by the * developer to add components to the user interface and initialize * non-component functionality. The component hierarchy must be initialized by * passing a {@link Component} with the main layout or other content of the view * to {@link #setContent(Component)} or to the constructor of the UI. *

* * @see #init(VaadinRequest) * @see UIProvider * * @since 7.0 */ public abstract class UI extends AbstractSingleComponentContainer implements Action.Notifier, PollNotifier, LegacyComponent, Focusable { /** * The application to which this UI belongs */ private volatile VaadinSession session; /** * List of windows in this UI. */ private final LinkedHashSet windows = new LinkedHashSet<>(); /** * The component that should be scrolled into view after the next repaint. * Null if nothing should be scrolled into view. */ private Component scrollIntoView; /** * The id of this UI, used to find the server side instance of the UI form * which a request originates. A negative value indicates that the UI id has * not yet been assigned by the Application. * * @see VaadinSession#getNextUIid() */ private int uiId = -1; /** * Keeps track of the Actions added to this component, and manages the * painting and handling as well. */ protected ActionManager actionManager; private ConnectorTracker connectorTracker = new ConnectorTracker(this); private Page page = new Page(this, getState(false).pageState); private LoadingIndicatorConfiguration loadingIndicatorConfiguration = new LoadingIndicatorConfigurationImpl( this); /** * Scroll Y position. */ private int scrollTop = 0; /** * Scroll X position */ private int scrollLeft = 0; private UIServerRpc rpc = new UIServerRpc() { @Override public void click(MouseEventDetails mouseDetails) { fireEvent(new ClickEvent(UI.this, mouseDetails)); } @Override public void resize(int viewWidth, int viewHeight, int windowWidth, int windowHeight) { // TODO We're not doing anything with the view dimensions getPage().updateBrowserWindowSize(windowWidth, windowHeight, true); } @Override public void scroll(int scrollTop, int scrollLeft) { UI.this.scrollTop = scrollTop; UI.this.scrollLeft = scrollLeft; } @Override public void poll() { fireEvent(new PollEvent(UI.this)); } @Override public void popstate(String uri) { getPage().updateLocation(uri, true, true); } }; private DebugWindowServerRpc debugRpc = new DebugWindowServerRpc() { @Override public void showServerDebugInfo(Connector connector) { String info = ConnectorHelper .getDebugInformation((ClientConnector) connector); getLogger().info(info); } @Override public void analyzeLayouts() { // TODO Move to client side List invalidSizes = ComponentSizeValidator .validateLayouts(UI.this); StringBuilder json = new StringBuilder(); json.append("{\"invalidLayouts\":"); json.append('['); if (invalidSizes != null) { boolean first = true; for (InvalidLayout invalidSize : invalidSizes) { if (!first) { json.append(','); } else { first = false; } invalidSize.reportErrors(json, System.err); } } json.append("]}"); getRpcProxy(DebugWindowClientRpc.class) .reportLayoutProblems(json.toString()); } @Override public void showServerDesign(Connector connector) { if (!(connector instanceof Component)) { getLogger().severe("Tried to output declarative design for " + connector + ", which is not a component"); return; } if (connector instanceof UI) { // We want to see the content of the UI, so we can add it to // another UI or component container connector = ((UI) connector).getContent(); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { Design.write((Component) connector, baos); getLogger().info("Design for " + connector + " requested from debug window:\n" + baos.toString(UTF_8.name())); } catch (IOException e) { getLogger().log(Level.WARNING, "Error producing design for " + connector, e); } } }; private WindowOrderRpc windowOrderRpc = windowOrders -> { Map orders = new LinkedHashMap<>(); for (Entry entry : windowOrders.entrySet()) { if (entry.getValue() instanceof Window) { orders.put(entry.getKey(), (Window) entry.getValue()); } } fireWindowOrder(orders); }; /** * Timestamp keeping track of the last heartbeat of this UI. Updated to the * current time whenever the application receives a heartbeat or UIDL * request from the client for this UI. */ private long lastHeartbeatTimestamp = System.currentTimeMillis(); private boolean closing = false; private TooltipConfiguration tooltipConfiguration = new TooltipConfigurationImpl( this); private PushConfiguration pushConfiguration = new PushConfigurationImpl( this); private ReconnectDialogConfiguration reconnectDialogConfiguration = new ReconnectDialogConfigurationImpl( this); private NotificationConfiguration notificationConfiguration = new NotificationConfigurationImpl( this); /** * Tracks which message from the client should come next. First message from * the client has id 0. */ private int lastProcessedClientToServerId = -1; /** * Stores the extension of the active drag source component */ private DragSourceExtension activeDragSource; // Despite being static final, a counter is not a constant. // CHECKSTYLE:OFF // Counter of DelayedCallbackRegistrations during application life-cycle. private static final AtomicLong delayedCallbackCounter = new AtomicLong(0); // CHECKSTYLE:ON private final Set pendingDelayedCallbacks = new LinkedHashSet<>(); private DelayedCallbackRpc delayedCallbackRpc = received -> { List toTrigger = new ArrayList<>(); for (InternalDelayedCallbackRegistration callback : pendingDelayedCallbacks) { Long id = callback.getId(); if (id <= received) { toTrigger.add(callback); } else { // LinkedHashSet maintains insertion order, registration IDs // increment, and registrations are always inserted // immediately, no need to go through the whole set break; } } toTrigger.forEach(registration -> registration.increment()); }; /** * Creates a new empty UI without a caption. The content of the UI must be * set by calling {@link #setContent(Component)} before using the UI. */ public UI() { this(null); } /** * Creates a new UI with the given component (often a layout) as its * content. * * @param content * the component to use as this UIs content. * * @see #setContent(Component) */ public UI(Component content) { registerRpc(rpc); registerRpc(debugRpc); registerRpc(windowOrderRpc); registerRpc(delayedCallbackRpc); setSizeFull(); setContent(content); } @Override protected UIState getState() { return (UIState) super.getState(); } @Override protected UIState getState(boolean markAsDirty) { return (UIState) super.getState(markAsDirty); } @Override public Class getStateType() { // This is a workaround for a problem with creating the correct state // object during build return UIState.class; } /** * Overridden to return a value instead of referring to the parent. * * @return this UI * * @see com.vaadin.ui.AbstractComponent#getUI() */ @Override public UI getUI() { return this; } /** * Gets the application object to which the component is attached. * *

* The method will return {@code null} if the component is not currently * attached to an application. *

* *

* Getting a null value is often a problem in constructors of regular * components and in the initializers of custom composite components. A * standard workaround is to use {@link VaadinSession#getCurrent()} to * retrieve the application instance that the current request relates to. * Another way is to move the problematic initialization to * {@link #attach()}, as described in the documentation of the method. *

* * @return the parent application of the component or null. * @see #attach() */ @Override public VaadinSession getSession() { return session; } @Override public void paintContent(PaintTarget target) throws PaintException { page.paintContent(target); if (scrollIntoView != null) { target.addAttribute("scrollTo", scrollIntoView); scrollIntoView = null; } if (pendingFocus != null) { // ensure focused component is still attached to this main window if (equals(pendingFocus.getUI()) || (pendingFocus.getUI() != null && equals(pendingFocus.getUI().getParent()))) { target.addAttribute("focused", pendingFocus); } pendingFocus = null; } if (actionManager != null) { actionManager.paintActions(null, target); } if (isResizeLazy()) { target.addAttribute(UIConstants.RESIZE_LAZY, true); } } /** * Fire a click event to all click listeners. * * @param object * The raw "value" of the variable change from the client side. */ private void fireClick(Map parameters) { MouseEventDetails mouseDetails = MouseEventDetails .deSerialize((String) parameters.get("mouseDetails")); fireEvent(new ClickEvent(this, mouseDetails)); } /** * Fire a window order event. * * @param windows * The windows with their orders whose order has been updated. */ private void fireWindowOrder(Map windows) { for (Entry entry : windows.entrySet()) { entry.getValue().fireWindowOrderChange(entry.getKey()); } fireEvent(new WindowOrderUpdateEvent(this, windows.values())); } @Override @SuppressWarnings("unchecked") public void changeVariables(Object source, Map variables) { if (variables.containsKey(EventId.CLICK_EVENT_IDENTIFIER)) { fireClick((Map) variables .get(EventId.CLICK_EVENT_IDENTIFIER)); } // Actions if (actionManager != null) { actionManager.handleActions(variables, this); } } /* * (non-Javadoc) * * @see com.vaadin.ui.HasComponents#iterator() */ @Override public Iterator iterator() { // TODO could directly create some kind of combined iterator instead of // creating a new ArrayList List components = new ArrayList<>(); if (getContent() != null) { components.add(getContent()); } components.addAll(windows); return Collections.unmodifiableCollection(components).iterator(); } /* * (non-Javadoc) * * @see com.vaadin.ui.ComponentContainer#getComponentCount() */ @Override public int getComponentCount() { return windows.size() + (getContent() == null ? 0 : 1); } /** * Sets the session to which this UI is assigned. *

* This method is for internal use by the framework. To explicitly close a * UI, see {@link #close()}. *

* * @param session * the session to set * * @throws IllegalStateException * if the session has already been set * * @see #getSession() */ public void setSession(VaadinSession session) { if (session == null && this.session == null) { throw new IllegalStateException( "Session should never be set to null when UI.session is already null"); } else if (session != null && this.session != null) { throw new IllegalStateException( "Session has already been set. Old session: " + getSessionDetails(this.session) + ". New session: " + getSessionDetails(session) + "."); } else { if (session == null) { try { detach(); } catch (Exception e) { getLogger().log(Level.WARNING, "Error while detaching UI from session", e); } // Disable push when the UI is detached. Otherwise the // push connection and possibly VaadinSession will live // on. getPushConfiguration().setPushMode(PushMode.DISABLED); new Thread(() -> { // This intentionally does disconnect without locking // the VaadinSession to avoid deadlocks where the server // uses a lock for the websocket connection // See https://dev.vaadin.com/ticket/18436 // The underlying problem is // https://dev.vaadin.com/ticket/16919 setPushConnection(null); }).start(); } this.session = session; } if (session != null) { attach(); } } private static String getSessionDetails(VaadinSession session) { if (session == null) { return null; } else { return session + " for " + session.getService().getServiceName(); } } /** * Gets the id of the UI, used to identify this UI within its application * when processing requests. The UI id should be present in every request to * the server that originates from this UI. * {@link VaadinService#findUI(VaadinRequest)} uses this id to find the * route to which the request belongs. *

* This method is not intended to be overridden. If it is overridden, care * should be taken since this method might be called in situations where * {@link UI#getCurrent()} does not return this UI. * * @return the id of this UI */ public int getUIId() { return uiId; } /** * Adds a window as a subwindow inside this UI. To open a new browser window * or tab, you should instead use a {@link UIProvider}. * * @param window * @throws IllegalArgumentException * if the window is already added to an application * @throws NullPointerException * if the given Window is null. */ public void addWindow(Window window) throws IllegalArgumentException, NullPointerException { if (window == null) { throw new NullPointerException("Argument must not be null"); } if (window.isAttached()) { throw new IllegalArgumentException( "Window is already attached to an application."); } attachWindow(window); } /** * Helper method to attach a window. * * @param w * the window to add */ private void attachWindow(Window w) { windows.add(w); w.setParent(this); fireComponentAttachEvent(w); markAsDirty(); } /** * Remove the given subwindow from this UI. * * Since Vaadin 6.5, {@link Window.CloseListener}s are called also when * explicitly removing a window by calling this method. * * Since Vaadin 6.5, returns a boolean indicating if the window was removed * or not. * * @param window * Window to be removed. * @return true if the subwindow was removed, false otherwise */ public boolean removeWindow(Window window) { if (!windows.remove(window)) { // Window window is not a subwindow of this UI. return false; } window.setParent(null); markAsDirty(); window.fireClose(); fireComponentDetachEvent(window); fireWindowOrder(Collections.singletonMap(-1, window)); return true; } /** * Gets all the windows added to this UI. * * @return an unmodifiable collection of windows */ public Collection getWindows() { return Collections.unmodifiableCollection(windows); } @Override public void focus() { super.focus(); } /** * Component that should be focused after the next repaint. Null if no focus * change should take place. */ private Focusable pendingFocus; private boolean resizeLazy = false; private Navigator navigator; private PushConnection pushConnection = null; private LocaleService localeService = new LocaleService(this, getState(false).localeServiceState); private String embedId; private String uiPathInfo; private String uiRootPath; private boolean mobileHtml5DndPolyfillLoaded; /** * This method is used by Component.Focusable objects to request focus to * themselves. Focus renders must be handled at window level (instead of * Component.Focusable) due we want the last focused component to be focused * in client too. Not the one that is rendered last (the case we'd get if * implemented in Focusable only). * * To focus component from Vaadin application, use Focusable.focus(). See * {@link Focusable}. * * @param focusable * to be focused on next paint */ public void setFocusedComponent(Focusable focusable) { pendingFocus = focusable; markAsDirty(); } /** * Scrolls any component between the component and UI to a suitable position * so the component is visible to the user. The given component must belong * to this UI. * * @param component * the component to be scrolled into view * @throws IllegalArgumentException * if {@code component} does not belong to this UI */ public void scrollIntoView(Component component) throws IllegalArgumentException { if (component.getUI() != this) { throw new IllegalArgumentException( "The component where to scroll must belong to this UI."); } scrollIntoView = component; markAsDirty(); } /** * Internal initialization method, should not be overridden. This method is * not declared as final because that would break compatibility with e.g. * CDI. * * @param request * the initialization request * @param uiId * the id of the new ui * @param embedId * the embed id of this UI, or null if no id is * known * * @see #getUIId() * @see #getEmbedId() */ public void doInit(VaadinRequest request, int uiId, String embedId) { if (this.uiId != -1) { String message = "This UI instance is already initialized (as UI id " + this.uiId + ") and can therefore not be initialized again (as UI id " + uiId + "). "; if (getSession() != null && !getSession().equals(VaadinSession.getCurrent())) { message += "Furthermore, it is already attached to another VaadinSession. "; } message += "Please make sure you are not accidentally reusing an old UI instance."; throw new IllegalStateException(message); } this.uiId = uiId; this.embedId = embedId; // Actual theme - used for finding CustomLayout templates setTheme(request.getParameter("theme")); getPage().init(request); String uiPathInfo = (String) request .getAttribute(ApplicationConstants.UI_ROOT_PATH); if (uiPathInfo != null) { setUiPathInfo(uiPathInfo); } if (getSession() != null && getSession().getConfiguration() != null && getSession().getConfiguration().isSendUrlsAsParameters() && getPage().getLocation() != null) { // By default the root is the URL from client String uiRootPath = getPage().getLocation().getPath(); if (uiPathInfo != null && uiRootPath.contains(uiPathInfo)) { // String everything from the URL after uiPathInfo // This will remove the navigation state from the URL uiRootPath = uiRootPath.substring(0, uiRootPath.indexOf(uiPathInfo) + uiPathInfo.length()); } else if (request.getPathInfo() != null) { // uiRootPath does not match the uiPathInfo // This can happen for example when embedding a Vaadin UI String pathInfo = request.getPathInfo(); if (uiRootPath.endsWith(pathInfo)) { uiRootPath = uiRootPath.substring(0, uiRootPath.length() - pathInfo.length()); } } // Store the URL as the UI Root Path setUiRootPath(uiRootPath); } // Call the init overridden by the application developer init(request); Navigator navigator = getNavigator(); if (navigator != null) { // Kickstart navigation if a navigator was attached in init() navigator.navigateTo(navigator.getState()); } } private void setUiRootPath(String uiRootPath) { this.uiRootPath = uiRootPath; } /** * Gets the part of path (from browser's URL) that points to this UI. * Basically the same as the value from {@link Page#getLocation()}, but * without possible view identifiers or path parameters. * * @return the part of path (from browser's URL) that points to this UI, * without possible view identifiers or path parameters * * @since 8.2 */ public String getUiRootPath() { return uiRootPath; } private void setUiPathInfo(String uiPathInfo) { this.uiPathInfo = uiPathInfo; } /** * Gets the path info part of the request that is used to detect the UI. * This is defined during UI init by certain {@link UIProvider UIProviders} * that map different UIs to different URIs, like Vaadin Spring. This * information is used by the {@link Navigator} when the {@link UI} is * annotated with {@link PushStateNavigation}. *

* For example if the UI is accessed through * {@code http://example.com/MyUI/mainview/parameter=1} the path info would * be {@code /MyUI}. * * @return the path info part of the request; {@code null} if no request * from client has been processed * * @since 8.2 */ public String getUiPathInfo() { return uiPathInfo; } /** * Initializes this UI. This method is intended to be overridden by * subclasses to build the view and configure non-component functionality. * Performing the initialization in a constructor is not suggested as the * state of the UI is not properly set up when the constructor is invoked. *

* The {@link VaadinRequest} can be used to get information about the * request that caused this UI to be created. *

* * @param request * the Vaadin request that caused this UI to be created */ protected abstract void init(VaadinRequest request); /** * Internal reinitialization method, should not be overridden. * * @since 7.2 * @param request * the request that caused this UI to be reloaded */ public void doRefresh(VaadinRequest request) { // This is a horrible hack. We want to have the most recent location and // browser window size available in refresh(), but we want to call // listeners, if any, only after refresh(). So we momentarily assign the // old values back before setting the new values again to ensure the // events are properly fired. PushConnection pushConnection = getPushConnection(); if (pushConnection instanceof AtmospherePushConnection && pushConnection.isConnected()) { pushConnection.disconnect(); } Page page = getPage(); URI oldLocation = page.getLocation(); int oldWidth = page.getBrowserWindowWidth(); int oldHeight = page.getBrowserWindowHeight(); page.init(request); // Reset heartbeat timeout to avoid surprise if it's almost expired setLastHeartbeatTimestamp(System.currentTimeMillis()); refresh(request); URI newLocation = page.getLocation(); int newWidth = page.getBrowserWindowWidth(); int newHeight = page.getBrowserWindowHeight(); page.updateLocation(oldLocation.toString(), false, false); page.updateBrowserWindowSize(oldWidth, oldHeight, false); page.updateLocation(newLocation.toString(), true, false); page.updateBrowserWindowSize(newWidth, newHeight, true); // Navigate if there is navigator, this is needed in case of // PushStateNavigation. Call navigateTo only if state have // truly changed Navigator navigator = getNavigator(); if (navigator != null && !Objects.equals(navigator.getCurrentNavigationState(), navigator.getState())) { navigator.navigateTo(navigator.getState()); } } /** * Reinitializes this UI after a browser refresh if the UI is set to be * preserved on refresh, typically using the {@link PreserveOnRefresh} * annotation. This method is intended to be overridden by subclasses if * needed; the default implementation is empty. *

* The {@link VaadinRequest} can be used to get information about the * request that caused this UI to be reloaded. * * @since 7.2 * @param request * the request that caused this UI to be reloaded */ protected void refresh(VaadinRequest request) { } /** * Sets the thread local for the current UI. This method is used by the * framework to set the current application whenever a new request is * processed and it is cleared when the request has been processed. *

* The application developer can also use this method to define the current * UI outside the normal request handling, e.g. when initiating custom * background threads. *

* The UI is stored using a weak reference to avoid leaking memory in case * it is not explicitly cleared. * * @param ui * the UI to register as the current UI * * @see #getCurrent() * @see ThreadLocal */ public static void setCurrent(UI ui) { CurrentInstance.set(UI.class, ui); } /** * Gets the currently used UI. The current UI is automatically defined when * processing requests to the server. In other cases, (e.g. from background * threads), the current UI is not automatically defined. *

* The UI is stored using a weak reference to avoid leaking memory in case * it is not explicitly cleared. * * @return the current UI instance if available, otherwise null * * @see #setCurrent(UI) */ public static UI getCurrent() { return CurrentInstance.get(UI.class); } /** * Set top offset to which the UI should scroll to. * * @param scrollTop */ public void setScrollTop(int scrollTop) { if (scrollTop < 0) { throw new IllegalArgumentException( "Scroll offset must be at least 0"); } if (this.scrollTop != scrollTop) { this.scrollTop = scrollTop; getRpcProxy(ScrollClientRpc.class).setScrollTop(scrollTop); } } public int getScrollTop() { return scrollTop; } /** * Set left offset to which the UI should scroll to. * * @param scrollLeft */ public void setScrollLeft(int scrollLeft) { if (scrollLeft < 0) { throw new IllegalArgumentException( "Scroll offset must be at least 0"); } if (this.scrollLeft != scrollLeft) { this.scrollLeft = scrollLeft; getRpcProxy(ScrollClientRpc.class).setScrollLeft(scrollLeft); } } public int getScrollLeft() { return scrollLeft; } @Override protected ActionManager getActionManager() { if (actionManager == null) { actionManager = new ActionManager(this); } return actionManager; } @Override public void addAction( T action) { getActionManager().addAction(action); } @Override public void removeAction( T action) { if (actionManager != null) { actionManager.removeAction(action); } } @Override public void addActionHandler(Handler actionHandler) { getActionManager().addActionHandler(actionHandler); } @Override public void removeActionHandler(Handler actionHandler) { if (actionManager != null) { actionManager.removeActionHandler(actionHandler); } } /** * Should resize operations be lazy, i.e. should there be a delay before * layout sizes are recalculated and resize events are sent to the server. * Speeds up resize operations in slow UIs with the penalty of slightly * decreased usability. *

* Default value: false *

*

* When there are active window resize listeners, lazy resize mode should be * used to avoid a large number of events during resize. *

* * @param resizeLazy * true to use a delay before recalculating sizes, false to * calculate immediately. */ public void setResizeLazy(boolean resizeLazy) { this.resizeLazy = resizeLazy; markAsDirty(); } /** * Checks whether lazy resize is enabled. * * @return true if lazy resize is enabled, false * if lazy resize is not enabled */ public boolean isResizeLazy() { return resizeLazy; } /** * Add a click listener to the UI. The listener is called whenever the user * clicks inside the UI. Also when the click targets a component inside the * UI, provided the targeted component does not prevent the click event from * propagating. * * @see Registration * * @param listener * The listener to add, not null * @return a registration object for removing the listener * @since 8.0 */ public Registration addClickListener(ClickListener listener) { return addListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class, listener, ClickListener.clickMethod); } /** * Remove a click listener from the UI. The listener should earlier have * been added using {@link #addClickListener(ClickListener)}. * * @param listener * The listener to remove * * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the * registration object returned from * {@link #removeClickListener(ClickListener)}. */ @Deprecated public void removeClickListener(ClickListener listener) { removeListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class, listener); } @Override public boolean isConnectorEnabled() { // TODO How can a UI be invisible? What does it mean? return isVisible() && isEnabled(); } public ConnectorTracker getConnectorTracker() { return connectorTracker; } public Page getPage() { return page; } /** * Returns the navigator attached to this UI or null if there is no * navigator. * * @return */ public Navigator getNavigator() { return navigator; } /** * For internal use only. * * @param navigator */ public void setNavigator(Navigator navigator) { this.navigator = navigator; } /** * Setting the caption of a UI is not supported. To set the title of the * HTML page, use Page.setTitle * * @deprecated As of 7.0, use {@link Page#setTitle(String)} */ @Override @Deprecated public void setCaption(String caption) { throw new UnsupportedOperationException( "You can not set the title of a UI. To set the title of the HTML page, use Page.setTitle"); } /** * Shows a notification message on the middle of the UI. The message * automatically disappears ("humanized message"). * * Care should be taken to to avoid XSS vulnerabilities as the caption is * rendered as html. * * @see #showNotification(Notification) * @see Notification * * @param caption * The message * * @deprecated As of 7.0, use Notification.show instead but be aware that * Notification.show does not allow HTML. */ @Deprecated public void showNotification(String caption) { Notification notification = new Notification(caption); notification.setHtmlContentAllowed(true);// Backwards compatibility getPage().showNotification(notification); } /** * Shows a notification message the UI. The position and behavior of the * message depends on the type, which is one of the basic types defined in * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE. * * Care should be taken to to avoid XSS vulnerabilities as the caption is * rendered as html. * * @see #showNotification(Notification) * @see Notification * * @param caption * The message * @param type * The message type * * @deprecated As of 7.0, use Notification.show instead but be aware that * Notification.show does not allow HTML. */ @Deprecated public void showNotification(String caption, Notification.Type type) { Notification notification = new Notification(caption, type); notification.setHtmlContentAllowed(true);// Backwards compatibility getPage().showNotification(notification); } /** * Shows a notification consisting of a bigger caption and a smaller * description on the middle of the UI. The message automatically disappears * ("humanized message"). * * Care should be taken to to avoid XSS vulnerabilities as the caption and * description are rendered as html. * * @see #showNotification(Notification) * @see Notification * * @param caption * The caption of the message * @param description * The message description * * @deprecated As of 7.0, use new Notification(...).show(Page) instead but * be aware that HTML by default not allowed. */ @Deprecated public void showNotification(String caption, String description) { Notification notification = new Notification(caption, description); notification.setHtmlContentAllowed(true);// Backwards compatibility getPage().showNotification(notification); } /** * Shows a notification consisting of a bigger caption and a smaller * description. The position and behavior of the message depends on the * type, which is one of the basic types defined in {@link Notification} , * for instance Notification.TYPE_WARNING_MESSAGE. * * Care should be taken to to avoid XSS vulnerabilities as the caption and * description are rendered as html. * * @see #showNotification(Notification) * @see Notification * * @param caption * The caption of the message * @param description * The message description * @param type * The message type * * @deprecated As of 7.0, use new Notification(...).show(Page) instead but * be aware that HTML by default not allowed. */ @Deprecated public void showNotification(String caption, String description, Notification.Type type) { Notification notification = new Notification(caption, description, type); notification.setHtmlContentAllowed(true);// Backwards compatibility getPage().showNotification(notification); } /** * Shows a notification consisting of a bigger caption and a smaller * description. The position and behavior of the message depends on the * type, which is one of the basic types defined in {@link Notification} , * for instance Notification.TYPE_WARNING_MESSAGE. * * Care should be taken to avoid XSS vulnerabilities if html content is * allowed. * * @see #showNotification(Notification) * @see Notification * * @param caption * The message caption * @param description * The message description * @param type * The type of message * @param htmlContentAllowed * Whether html in the caption and description should be * displayed as html or as plain text * * @deprecated As of 7.0, use new Notification(...).show(Page). */ @Deprecated public void showNotification(String caption, String description, Notification.Type type, boolean htmlContentAllowed) { getPage().showNotification(new Notification(caption, description, type, htmlContentAllowed)); } /** * Shows a notification message. * * @see Notification * @see #showNotification(String) * @see #showNotification(String, String) * * @param notification * The notification message to show * * @deprecated As of 7.0, use Notification.show instead */ @Deprecated public void showNotification(Notification notification) { getPage().showNotification(notification); } /** * Returns the timestamp of the last received heartbeat for this UI. *

* This method is not intended to be overridden. If it is overridden, care * should be taken since this method might be called in situations where * {@link UI#getCurrent()} does not return this UI. * * @see VaadinService#closeInactiveUIs(VaadinSession) * * @return The time the last heartbeat request occurred, in milliseconds * since the epoch. */ public long getLastHeartbeatTimestamp() { return lastHeartbeatTimestamp; } /** * Sets the last heartbeat request timestamp for this UI. Called by the * framework whenever the application receives a valid heartbeat request for * this UI. *

* This method is not intended to be overridden. If it is overridden, care * should be taken since this method might be called in situations where * {@link UI#getCurrent()} does not return this UI. * * @param lastHeartbeat * The time the last heartbeat request occurred, in milliseconds * since the epoch. */ public void setLastHeartbeatTimestamp(long lastHeartbeat) { lastHeartbeatTimestamp = lastHeartbeat; } /** * Gets the theme currently in use by this UI. * * @return the theme name */ public String getTheme() { return getState(false).theme; } /** * Sets the theme currently in use by this UI *

* Calling this method will remove the old theme (CSS file) from the * application and add the new theme. *

* Note that this method is NOT SAFE to call in a portal environment or * other environment where there are multiple UIs on the same page. The old * CSS file will be removed even if there are other UIs on the page which * are still using it. * * @since 7.3 * @param theme * The new theme name */ public void setTheme(String theme) { if (theme == null) { getState().theme = null; } else { getState().theme = VaadinServlet.stripSpecialChars(theme); } } /** * Marks this UI to be {@link #detach() detached} from the session at the * end of the current request, or the next request if there is no current * request (if called from a background thread, for instance.) *

* The UI is detached after the response is sent, so in the current request * it can still update the client side normally. However, after the response * any new requests from the client side to this UI will cause an error, so * usually the client should be asked, for instance, to reload the page * (serving a fresh UI instance), to close the page, or to navigate * somewhere else. *

* Note that this method is strictly for users to explicitly signal the * framework that the UI should be detached. Overriding it is not a reliable * way to catch UIs that are to be detached. Instead, {@code UI.detach()} * should be overridden or a {@link DetachListener} used. */ public void close() { closing = true; boolean sessionExpired = (session == null || session.getState() != State.OPEN); getRpcProxy(UIClientRpc.class).uiClosed(sessionExpired); if (getPushConnection() != null) { // Push the Rpc to the client. The connection will be closed when // the UI is detached and cleaned up. // Can't use UI.push() directly since it checks for a valid session if (session != null) { session.getService().runPendingAccessTasks(session); } getPushConnection().push(); } } /** * Returns whether this UI is marked as closed and is to be detached. *

* This method is not intended to be overridden. If it is overridden, care * should be taken since this method might be called in situations where * {@link UI#getCurrent()} does not return this UI. * * @see #close() * * @return whether this UI is closing. */ public boolean isClosing() { return closing; } /** * Called after the UI is added to the session. A UI instance is attached * exactly once, before its {@link #init(VaadinRequest) init} method is * called. * * @see Component#attach */ @Override public void attach() { super.attach(); getLocaleService().addLocale(getLocale()); } /** * Called before the UI is removed from the session. A UI instance is * detached exactly once, either: *

    *
  • after it is explicitly {@link #close() closed}. *
  • when its session is closed or expires *
  • after three missed heartbeat requests. *
*

* Note that when a UI is detached, any changes made in the {@code detach} * methods of any children or {@link DetachListener}s that would be * communicated to the client are silently ignored. */ @Override public void detach() { if (!pendingDelayedCallbacks.isEmpty()) { getLogger().info( "UI detached before all pending callbacks could be processed. " + "Cancelling the remaining callbacks."); // each call to #cancel() modifies the set, iterate over a copy for (DelayedCallbackRegistration registration : new LinkedHashSet<>( pendingDelayedCallbacks)) { registration.cancel(); } // should be empty already but just in case pendingDelayedCallbacks.clear(); } super.detach(); } /* * (non-Javadoc) * * @see * com.vaadin.ui.AbstractSingleComponentContainer#setContent(com.vaadin. * ui.Component) */ @Override public void setContent(Component content) { if (content instanceof Window) { throw new IllegalArgumentException( "A Window cannot be added using setContent. Use addWindow(Window window) instead"); } super.setContent(content); } @Override public void setTabIndex(int tabIndex) { getState().tabIndex = tabIndex; } @Override public int getTabIndex() { return getState(false).tabIndex; } /** * Locks the session of this UI and runs the provided Runnable right away. *

* It is generally recommended to use {@link #access(Runnable)} instead of * this method for accessing a session from a different thread as * {@link #access(Runnable)} can be used while holding the lock of another * session. To avoid causing deadlocks, this methods throws an exception if * it is detected than another session is also locked by the current thread. *

* This method behaves differently than {@link #access(Runnable)} in some * situations: *
    *
  • If the current thread is currently holding the lock of the session, * {@link #accessSynchronously(Runnable)} runs the task right away whereas * {@link #access(Runnable)} defers the task to a later point in time.
  • *
  • If some other thread is currently holding the lock for the session, * {@link #accessSynchronously(Runnable)} blocks while waiting for the lock * to be available whereas {@link #access(Runnable)} defers the task to a * later point in time.
  • *
* * @since 7.1 * * @param runnable * the runnable which accesses the UI * @throws UIDetachedException * if the UI is not attached to a session (and locking can * therefore not be done) * @throws IllegalStateException * if the current thread holds the lock for another session * * @see #access(Runnable) * @see VaadinSession#accessSynchronously(Runnable) */ public void accessSynchronously(Runnable runnable) throws UIDetachedException { Map, CurrentInstance> old = null; VaadinSession session = getSession(); if (session == null) { throw new UIDetachedException(); } VaadinService.verifyNoOtherSessionLocked(session); session.lock(); try { if (getSession() == null) { // UI was detached after fetching the session but before we // acquired the lock. throw new UIDetachedException(); } old = CurrentInstance.setCurrent(this); runnable.run(); } finally { session.unlock(); if (old != null) { CurrentInstance.restoreInstances(old); } } } /** * Provides exclusive access to this UI from outside a request handling * thread. *

* The given runnable is executed while holding the session lock to ensure * exclusive access to this UI. If the session is not locked, the lock will * be acquired and the runnable is run right away. If the session is * currently locked, the runnable will be run before that lock is released. *

*

* RPC handlers for components inside this UI do not need to use this method * as the session is automatically locked by the framework during RPC * handling. *

*

* Please note that the runnable might be invoked on a different thread or * later on the current thread, which means that custom thread locals might * not have the expected values when the command is executed. * {@link UI#getCurrent()}, {@link VaadinSession#getCurrent()} and * {@link VaadinService#getCurrent()} are set according to this UI before * executing the command. Other standard CurrentInstance values such as * {@link VaadinService#getCurrentRequest()} and * {@link VaadinService#getCurrentResponse()} will not be defined. *

*

* The returned future can be used to check for task completion and to * cancel the task. *

* * @see #getCurrent() * @see #accessSynchronously(Runnable) * @see VaadinSession#access(Runnable) * @see VaadinSession#lock() * * @since 7.1 * * @param runnable * the runnable which accesses the UI * @throws UIDetachedException * if the UI is not attached to a session (and locking can * therefore not be done) * @return a future that can be used to check for task completion and to * cancel the task */ public Future access(final Runnable runnable) { VaadinSession session = getSession(); if (session == null) { throw new UIDetachedException(); } return session.access(new ErrorHandlingRunnable() { @Override public void run() { accessSynchronously(runnable); } @Override public void handleError(Exception exception) { try { exception = ErrorHandlingRunnable.processException(runnable, exception); if (exception instanceof UIDetachedException) { assert session != null; /* * UI was detached after access was run, but before * accessSynchronously. Furthermore, there wasn't an * ErrorHandlingRunnable that handled the exception. */ getLogger().log(Level.WARNING, "access() task ignored " + "because UI got detached after the task was " + "enqueued. To suppress this message, change " + "the task to implement {0} and make it handle " + "{1}. Affected task: {2}", new Object[] { ErrorHandlingRunnable.class.getName(), UIDetachedException.class.getName(), runnable }); } else if (exception != null) { /* * If no ErrorHandlingRunnable, or if it threw an * exception of its own. */ ConnectorErrorEvent errorEvent = new ConnectorErrorEvent( UI.this, exception); ErrorHandler errorHandler = com.vaadin.server.ErrorEvent .findErrorHandler(UI.this); if (errorHandler == null && getSession() == null) { /* * Special case where findErrorHandler(UI) cannot * find the session handler because the UI has * recently been detached. */ errorHandler = com.vaadin.server.ErrorEvent .findErrorHandler(session); } if (errorHandler == null) { errorHandler = new DefaultErrorHandler(); } errorHandler.error(errorEvent); } } catch (Exception e) { getLogger().log(Level.SEVERE, e.getMessage(), e); } } }); } /** * Retrieves the object used for configuring tooltips. * * @return The instance used for tooltip configuration */ public TooltipConfiguration getTooltipConfiguration() { return tooltipConfiguration; } /** * Retrieves the object used for configuring notifications. * * @return The instance used for notification configuration */ public NotificationConfiguration getNotificationConfiguration() { return notificationConfiguration; } /** * Retrieves the object used for configuring the loading indicator. * * @return The instance used for configuring the loading indicator */ public LoadingIndicatorConfiguration getLoadingIndicatorConfiguration() { return loadingIndicatorConfiguration; } /** * Pushes the pending changes and client RPC invocations of this UI to the * client-side. *

* If push is enabled, but the push connection is not currently open, the * push will be done when the connection is established. *

* As with all UI methods, the session must be locked when calling this * method. It is also recommended that {@link UI#getCurrent()} is set up to * return this UI since writing the response may invoke logic in any * attached component or extension. The recommended way of fulfilling these * conditions is to use {@link #access(Runnable)}. * * @throws IllegalStateException * if push is disabled. * @throws UIDetachedException * if this UI is not attached to a session. * * @see #getPushConfiguration() * * @since 7.1 */ public void push() { VaadinSession session = getSession(); if (session == null) { throw new UIDetachedException("Cannot push a detached UI"); } assert session.hasLock(); if (!getPushConfiguration().getPushMode().isEnabled()) { throw new IllegalStateException("Push not enabled"); } assert pushConnection != null; /* * Purge the pending access queue as it might mark a connector as dirty * when the push would otherwise be ignored because there are no changes * to push. */ session.getService().runPendingAccessTasks(session); if (!getConnectorTracker().hasDirtyConnectors()) { // Do not push if there is nothing to push return; } pushConnection.push(); } /** * Returns the internal push connection object used by this UI. This method * should only be called by the framework. *

* This method is not intended to be overridden. If it is overridden, care * should be taken since this method might be called in situations where * {@link UI#getCurrent()} does not return this UI. * * @return the push connection used by this UI, or {@code null} if push is * not available. */ public PushConnection getPushConnection() { assert !(getPushConfiguration().getPushMode().isEnabled() && pushConnection == null); return pushConnection; } /** * Sets the internal push connection object used by this UI. This method * should only be called by the framework. *

* The {@code pushConnection} argument must be non-null if and only if * {@code getPushConfiguration().getPushMode().isEnabled()}. * * @param pushConnection * the push connection to use for this UI */ public void setPushConnection(PushConnection pushConnection) { // If pushMode is disabled then there should never be a pushConnection; // if enabled there should always be assert (pushConnection == null) ^ getPushConfiguration().getPushMode().isEnabled(); if (pushConnection == this.pushConnection) { return; } if (this.pushConnection != null && this.pushConnection.isConnected()) { this.pushConnection.disconnect(); } this.pushConnection = pushConnection; } /** * Sets the interval with which the UI should poll the server to see if * there are any changes. Polling is disabled by default. *

* Note that it is possible to enable push and polling at the same time but * it should not be done to avoid excessive server traffic. *

*

* Add-on developers should note that this method is only meant for the * application developer. An add-on should not set the poll interval * directly, rather instruct the user to set it. *

* * @param intervalInMillis * The interval (in ms) with which the UI should poll the server * or -1 to disable polling */ public void setPollInterval(int intervalInMillis) { getState().pollInterval = intervalInMillis; } /** * Returns the interval with which the UI polls the server. * * @return The interval (in ms) with which the UI polls the server or -1 if * polling is disabled */ public int getPollInterval() { return getState(false).pollInterval; } @Override public Registration addPollListener(PollListener listener) { return addListener(EventId.POLL, PollEvent.class, listener, PollListener.POLL_METHOD); } @Override @Deprecated public void removePollListener(PollListener listener) { removeListener(EventId.POLL, PollEvent.class, listener); } /** * Retrieves the object used for configuring the push channel. * * @since 7.1 * @return The instance used for push configuration */ public PushConfiguration getPushConfiguration() { return pushConfiguration; } /** * Retrieves the object used for configuring the reconnect dialog. * * @since 7.6 * @return The instance used for reconnect dialog configuration */ public ReconnectDialogConfiguration getReconnectDialogConfiguration() { return reconnectDialogConfiguration; } /** * Get the label that is added to the container element, where tooltip, * notification and dialogs are added to. * * @return the label of the container */ public String getOverlayContainerLabel() { return getState(false).overlayContainerLabel; } /** * Sets the label that is added to the container element, where tooltip, * notifications and dialogs are added to. *

* This is helpful for users of assistive devices, as this element is * reachable for them. *

* * @param overlayContainerLabel * label to use for the container */ public void setOverlayContainerLabel(String overlayContainerLabel) { getState().overlayContainerLabel = overlayContainerLabel; } /** * Returns the locale service which handles transmission of Locale data to * the client. * * @since 7.1 * @return The LocaleService for this UI */ public LocaleService getLocaleService() { return localeService; } private static Logger getLogger() { return Logger.getLogger(UI.class.getName()); } /** * Gets a string the uniquely distinguishes this UI instance based on where * it is embedded. The embed identifier is based on the * window.name DOM attribute of the browser window where the UI * is displayed and the id of the div element where the UI is embedded. * * @since 7.2 * @return the embed id for this UI, or null if no id known */ public String getEmbedId() { return embedId; } /** * Gets the last processed server message id. * * Used internally for communication tracking. * * @return lastProcessedServerMessageId the id of the last processed server * message * @since 7.6 */ public int getLastProcessedClientToServerId() { return lastProcessedClientToServerId; } /** * Sets the last processed server message id. * * Used internally for communication tracking. * * @param lastProcessedClientToServerId * the id of the last processed server message * @since 7.6 */ public void setLastProcessedClientToServerId( int lastProcessedClientToServerId) { this.lastProcessedClientToServerId = lastProcessedClientToServerId; } /** * Adds a WindowOrderUpdateListener to the UI. *

* The WindowOrderUpdateEvent is fired when the order positions of windows * are updated. It can happen when some window (this or other) is brought to * front or detached. *

* The other way to listen window position for specific window is * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)} * * @see Window#addWindowOrderChangeListener(WindowOrderChangeListener) * * @param listener * the WindowModeChangeListener to add. * @since 8.0 * * @return a registration object for removing the listener */ public Registration addWindowOrderUpdateListener( WindowOrderUpdateListener listener) { addListener(EventId.WINDOW_ORDER, WindowOrderUpdateEvent.class, listener, WindowOrderUpdateListener.windowOrderUpdateMethod); return () -> removeListener(EventId.WINDOW_ORDER, WindowOrderUpdateEvent.class, listener); } /** * Sets the drag source of an active HTML5 drag event. * * @param extension * Extension of the drag source component. * @see DragSourceExtension * @since 8.1 */ public void setActiveDragSource( DragSourceExtension extension) { activeDragSource = extension; } /** * Gets the drag source of an active HTML5 drag event. * * @return Extension of the drag source component if the drag event is * active and originated from this UI, {@literal null} otherwise. * @see DragSourceExtension * @since 8.1 */ public DragSourceExtension getActiveDragSource() { return activeDragSource; } /** * Returns whether HTML5 DnD extensions {@link DragSourceExtension} and * {@link DropTargetExtension} and alike should be enabled for mobile * devices. *

* By default, it is disabled. * * @return {@code true} if enabled, {@code false} if not * @since 8.1 * @see #setMobileHtml5DndEnabled(boolean) */ public boolean isMobileHtml5DndEnabled() { return getState(false).enableMobileHTML5DnD; } /** * Enable or disable HTML5 DnD for mobile devices. *

* Usually you should enable the support in the {@link #init(VaadinRequest)} * method. By default, it is disabled. This operation is NOOP when the user * is not on a mobile device. *

* Changing this will effect all {@link DragSourceExtension} and * {@link DropTargetExtension} (and subclasses) that have not yet been * attached to the UI on the client side. *

* NOTE: When disabling this after it has been enabled, it will not * affect {@link DragSourceExtension} and {@link DropTargetExtension} (and * subclasses) that have been previously added. Those extensions should be * explicitly removed to make sure user cannot perform DnD operations * anymore. * * @param enabled * {@code true} if enabled, {@code false} if not * @since 8.1 */ public void setMobileHtml5DndEnabled(boolean enabled) { if (getState(false).enableMobileHTML5DnD != enabled) { getState().enableMobileHTML5DnD = enabled; if (isMobileHtml5DndEnabled()) { if (VaadinService.getCurrentRequest() == null) { getState().enableMobileHTML5DnD = false; throw new IllegalStateException("HTML5 DnD cannot be " + "enabled for mobile devices when current " + "VaadinRequest cannot be accessed. Call this " + "method from init(VaadinRequest) to ensure access."); } loadMobileHtml5DndPolyfill(); } } } /** * Load and initialize the mobile drag-drop-polyfill if needed and not yet * done so. */ private void loadMobileHtml5DndPolyfill() { if (mobileHtml5DndPolyfillLoaded) { return; } if (!getPage().getWebBrowser().isTouchDevice()) { return; } mobileHtml5DndPolyfillLoaded = true; String vaadinLocation = getSession().getService().getStaticFileLocation( VaadinService.getCurrentRequest()) + "/VAADIN/"; getPage().addDependency(new Dependency(Type.JAVASCRIPT, vaadinLocation + ApplicationConstants.MOBILE_DND_POLYFILL_JS)); getRpcProxy(PageClientRpc.class).initializeMobileHtml5DndPolyfill(); } /** * Returns whether LayoutManager uses thorough size check that evaluates the * presence of the element and uses calculated size, or defaults to a * slightly faster check that can result in incorrect size information if * the check is triggered while a transform animation is ongoing. This can * happen e.g. when a PopupView is opened. *

* By default, the thorough size check is enabled. * * @return {@code true} if thorough size check enabled, {@code false} if not * @since 8.13 */ public boolean isUsingThoroughSizeCheck() { return getState(false).thoroughSizeCheck; } /** * Set whether LayoutManager should use thorough size check that evaluates * the presence of the element and uses calculated size, or default to a * slightly faster check that can result in incorrect size information if * the check is triggered while a transform animation is ongoing. This can * happen e.g. when a PopupView is opened. *

* By default, the thorough size check is enabled. * * @param thoroughSizeCheck * {@code true} if thorough size check enabled, {@code false} if * not * @since 8.13 */ public void setUsingThoroughSizeCheck(boolean thoroughSizeCheck) { getState().thoroughSizeCheck = thoroughSizeCheck; } /** * Executes the given callback function after a single client round-trip. If * this UI gets detached before he callback gets processed, execution is * cancelled. * * @since 8.18 * @param callback * the callback to trigger after the round-trip * @return registration object that can be used to cancel the callback */ public DelayedCallbackRegistration runAfterRoundTrip( DelayedCallback callback) { return runAfterRoundTrip(callback, 1); } /** * Executes the given callback function after the given number of client * round-trips. If this UI gets detached before he callback gets processed, * execution is cancelled. *

* Note: it is not recommended to use a large number as a way of simulating * a delay. * * @since 8.18 * @param callback * the callback to trigger after the configured round-trips * @param numberOfRoundTripsToWait * how many round-trips should be waited for before triggering * the callback * @return registration object that can be used to cancel the callback */ public DelayedCallbackRegistration runAfterRoundTrip( DelayedCallback callback, int numberOfRoundTripsToWait) { return runAfterRoundTrip(callback, null, numberOfRoundTripsToWait); } /** * Executes the given callback function after a single client round-trip, or * the cancel callback if the execution is cancelled through * {@link DelayedCallbackRegistration#cancel()}. The framework cancels the * execution if this UI gets detached before he callback gets processed, but * the method can also be called by implementing application's code. * * @since 8.18 * @param callback * the callback to trigger after the round-trip * @param cancelCallback * the callback to trigger if the execution gets cancelled * @return registration object that can be used to cancel the callback */ public DelayedCallbackRegistration runAfterRoundTrip( DelayedCallback callback, DelayedCallback cancelCallback) { return runAfterRoundTrip(callback, 1); } /** * Executes the given callback function after the given number of client * round-trips, or the cancel callback if the execution is cancelled through * {@link DelayedCallbackRegistration#cancel()}. The framework cancels the * execution if this UI gets detached before he callback gets processed, but * the method can also be called by implementing application's code. *

* Note: it is not recommended to use a large number or round-trips as a way * of simulating a delay. * * @since 8.18 * @param callback * the callback to trigger after the configured round-trips * @param cancelCallback * the callback to trigger if the execution gets cancelled * @param numberOfRoundTripsToWait * how many round-trips should be waited for before triggering * the callback * @return registration object that can be used to cancel the callback */ public DelayedCallbackRegistration runAfterRoundTrip( DelayedCallback callback, DelayedCallback cancelCallback, int numberOfRoundTripsToWait) { if (numberOfRoundTripsToWait <= 0) { throw new IllegalArgumentException( "Number of round-trips must be greater than zero."); } InternalDelayedCallbackRegistration registration = new InternalDelayedCallbackRegistration() { @Override void increment() { completed++; // always remove, even if more round-trips pending pendingDelayedCallbacks.remove(this); if (completed < numberOfRoundTripsToWait) { // new round-trip ahead, update ID and re-add id = delayedCallbackCounter.incrementAndGet(); pendingDelayedCallbacks.add(this); // always use the newest, might have increased already getState().latestDelayedCallbackID = delayedCallbackCounter .get(); } else { executed = true; callback.perform(); } } @Override public boolean cancel() { if (super.cancel()) { if (cancelCallback != null) { cancelCallback.perform(); } return true; } return false; } }; pendingDelayedCallbacks.add(registration); // always use the newest, might have increased already getState().latestDelayedCallbackID = delayedCallbackCounter.get(); return registration; } /** * An interface for the callback in * {@link #runAfterRoundTrip(DelayedCallback)}. *

* Usage examples: *

* {@code ui.runAfterRoundTrip(() -> textField.focus())} *

* {@code ui.runAfterRoundTrip(() -> action when successful, () -> * action when cancelled)} * * @since 8.18 */ public interface DelayedCallback extends Serializable { public void perform(); } /** * An interface used for keeping track of callback requests registered via * {@link #runAfterRoundTrip(DelayedCallback)}. * * @since 8.18 */ public interface DelayedCallbackRegistration extends Serializable { /** * Returns {@code true} if the callback operation is still waiting to be * executed. * * @return {@code true} if callback still waiting, {@code false} * otherwise */ public boolean isPending(); /** * Returns {@code true} if the delay has been completed and the callback * operation has been executed. * * @return {@code true} if callback executed, {@code false} otherwise */ public boolean hasExecuted(); /** * Returns {@code true} if the callback operation was cancelled * successfully before it could be executed. * * @return {@code true} if cancelled successfully, {@code false} * otherwise * @see #cancel() */ public boolean isCancelled(); /** * Cancels the pending callback operation and stops this registration * object from triggering any further round-trips. Does nothing if the * callback operation has been executed already. *

* Note: calling this method does not prevent round-trips getting * triggered for other reasons. * * @return {@code true} if cancelling was successful, {@code false} * otherwise */ public boolean cancel(); /** * Returns the current ID of this registration object. If the * registration has been configured to wait for multiple round-trips, * the ID gets updated for each round. * * @return current ID */ public long getId(); } /* * Private to avoid exposing implementation details or #increment() outside * of this class. */ private abstract class InternalDelayedCallbackRegistration implements DelayedCallbackRegistration { protected long id = delayedCallbackCounter.incrementAndGet(); protected boolean cancelled = false; protected boolean executed = false; protected int completed = 0; @Override public boolean isCancelled() { return cancelled; } @Override public boolean isPending() { return !executed && !cancelled; } @Override public boolean hasExecuted() { return executed; } @Override public long getId() { return id; } @Override public boolean cancel() { if (!executed) { cancelled = true; pendingDelayedCallbacks.remove(this); return true; } return false; } abstract void increment(); } /** * Event which is fired when the ordering of the windows is updated. *

* The other way to listen window position for specific window is * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)} * * @see Window.WindowOrderChangeEvent * * @author Vaadin Ltd * @since 8.0 * */ public static class WindowOrderUpdateEvent extends Component.Event { private final Collection windows; public WindowOrderUpdateEvent(Component source, Collection windows) { super(source); this.windows = windows; } /** * Gets the windows in the order they appear in the UI: top most window * is first, bottom one last. * * @return the windows collection */ public Collection getWindows() { return windows; } } /** * An interface used for listening to Windows order update events. * * @since 8.0 * * @see Window.WindowOrderChangeEvent */ @FunctionalInterface public interface WindowOrderUpdateListener extends ConnectorEventListener { public static final Method windowOrderUpdateMethod = ReflectTools .findMethod(WindowOrderUpdateListener.class, "windowOrderUpdated", WindowOrderUpdateEvent.class); /** * Called when the windows order positions are changed. Use * {@link WindowOrderUpdateEvent#getWindows()} to get a reference to the * {@link Window}s whose order positions are updated. Use * {@link Window#getOrderPosition()} to get window position for specific * window. * * @param event */ public void windowOrderUpdated(WindowOrderUpdateEvent event); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy