/* * Copyright 2011 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.terminal.gwt.client.ui; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.BodyElement; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Position; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.DomEvent.Type; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.event.shared.EventHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.BrowserInfo; import com.vaadin.terminal.gwt.client.Container; import com.vaadin.terminal.gwt.client.Focusable; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.RenderSpace; import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VConsole; import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; /** * */ public class VView extends SimplePanel implements Container, ResizeHandler, Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable { public static final String BROWSER_HEIGHT_VAR = "browserHeight"; public static final String BROWSER_WIDTH_VAR = "browserWidth"; private static final String CLASSNAME = "v-view"; public static final String NOTIFICATION_HTML_CONTENT_NOT_ALLOWED = "useplain"; private static int MONITOR_PARENT_TIMER_INTERVAL = 1000; private String theme; private Paintable layout; private final LinkedHashSet subWindows = new LinkedHashSet(); private String id; private 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; private 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; private int scrollTop; private int scrollLeft; private boolean rendering; private boolean scrollable; private boolean immediate; private boolean resizeLazy = false; public static final String RESIZE_LAZY = "rL"; /** * Reference to the parent frame/iframe. Null if there is no parent (i)frame * or if the application and parent frame are in different domains. */ private Element parentFrame; private ClickEventHandler clickEventHandler = new ClickEventHandler(this, VPanel.CLICK_EVENT_IDENTIFIER) { @Override protected HandlerRegistration registerHandler( H handler, Type type) { return addDomHandler(handler, type); } }; private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200, new ScheduledCommand() { public void execute() { performSizeCheck(); } }); public VView() { super(); setStyleName(CLASSNAME); // Allow focusing the view by using the focus() method, the view // should not be in the document focus flow getElement().setTabIndex(-1); TouchScrollDelegate.enableTouchScrolling(this, getElement()); } /** * 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) { boolean changed = false; if (windowWidth != newWindowWidth) { windowWidth = newWindowWidth; changed = true; VConsole.log("New window width: " + windowWidth); } if (windowHeight != newWindowHeight) { windowHeight = newWindowHeight; changed = true; VConsole.log("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; VConsole.log("New parent width: " + parentWidth); } if (parentHeight != newParentHeight) { parentHeight = newParentHeight; changed = true; VConsole.log("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.) */ VConsole.log("Running layout functions due to window or parent resize"); connection.runDescendentsLayout(VView.this); Util.runWebkitOverflowAutoFix(getElement()); // 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(); } } public String getTheme() { return theme; } /** * Used to reload host page on theme changes. */ private static native void reloadHostPage() /*-{ $wnd.location.reload(); }-*/; /** * Evaluate the given script in the browser document. * * @param script * Script to be executed. */ private static native void eval(String script) /*-{ try { if (script == null) return; $wnd.eval(script); } catch (e) { } }-*/; /** * 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(ApplicationConnection.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(); } private native void open(String url, String name) /*-{ $wnd.open(url, name); }-*/; public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) { rendering = true; id = uidl.getId(); boolean firstPaint = connection == null; connection = client; immediate = uidl.hasAttribute("immediate"); resizeLazy = uidl.hasAttribute(RESIZE_LAZY); String newTheme = uidl.getStringAttribute("theme"); if (theme != null && !newTheme.equals(theme)) { // Complete page refresh is needed due css can affect layout // calculations etc reloadHostPage(); } else { theme = newTheme; } if (uidl.hasAttribute("style")) { setStyleName(getStylePrimaryName() + " " + uidl.getStringAttribute("style")); } if (uidl.hasAttribute("name")) { client.setWindowName(uidl.getStringAttribute("name")); } clickEventHandler.handleEventHandlerRegistration(client); if (!isEmbedded()) { // only change window title if we're in charge of the whole page com.google.gwt.user.client.Window.setTitle(uidl .getStringAttribute("caption")); } // Process children int childIndex = 0; // Open URL:s boolean isClosed = false; // was this window closed? while (childIndex < uidl.getChildCount() && "open".equals(uidl.getChildUIDL(childIndex).getTag())) { final UIDL open = uidl.getChildUIDL(childIndex); final String url = client.translateVaadinUri(open .getStringAttribute("src")); final String target = open.getStringAttribute("name"); if (target == null) { // source will be opened to this browser window, but we may have // to finish rendering this window in case this is a download // (and window stays open). Scheduler.get().scheduleDeferred(new Command() { public void execute() { goTo(url); } }); } else if ("_self".equals(target)) { // This window is closing (for sure). Only other opens are // relevant in this change. See #3558, #2144 isClosed = true; goTo(url); } else { String options; boolean alwaysAsPopup = true; if (open.hasAttribute("popup")) { alwaysAsPopup = open.getBooleanAttribute("popup"); } if (alwaysAsPopup) { if (open.hasAttribute("border")) { if (open.getStringAttribute("border").equals("minimal")) { options = "menubar=yes,location=no,status=no"; } else { options = "menubar=no,location=no,status=no"; } } else { options = "resizable=yes,menubar=yes,toolbar=yes,directories=yes,location=yes,scrollbars=yes,status=yes"; } if (open.hasAttribute("width")) { int w = open.getIntAttribute("width"); options += ",width=" + w; } if (open.hasAttribute("height")) { int h = open.getIntAttribute("height"); options += ",height=" + h; } Window.open(url, target, options); } else { open(url, target); } } childIndex++; } if (isClosed) { // don't render the content, something else will be opened to this // browser view rendering = false; return; } // Draw this application level window UIDL childUidl = uidl.getChildUIDL(childIndex); final Paintable lo = client.getPaintable(childUidl); if (layout != null) { if (layout != lo) { // remove old client.unregisterPaintable(layout); // add new setWidget((Widget) lo); layout = lo; } } else { setWidget((Widget) lo); layout = lo; } boolean childLayoutCached = childUidl.getBooleanAttribute("cached"); if (!childLayoutCached) { // Liferay #5521: update portlet-body element size updateLiferayPortletBodySize(childUidl); } layout.updateFromUIDL(childUidl, client); if (!childLayoutCached) { updateParentFrameSize(); } // Save currently open subwindows to track which will need to be closed final HashSet removedSubWindows = new HashSet( subWindows); // Handle other UIDL children while ((childUidl = uidl.getChildUIDL(++childIndex)) != null) { String tag = childUidl.getTag().intern(); if (tag == "actions") { if (actionHandler == null) { actionHandler = new ShortcutActionHandler(id, client); } actionHandler.updateActionMap(childUidl); } else if (tag == "execJS") { String script = childUidl.getStringAttribute("script"); eval(script); } else if (tag == "notifications") { for (final Iterator it = childUidl.getChildIterator(); it .hasNext();) { final UIDL notification = (UIDL) it.next(); VNotification.showNotification(client, notification); } } else { // subwindows final Paintable w = client.getPaintable(childUidl); if (subWindows.contains(w)) { removedSubWindows.remove(w); } else { subWindows.add((VWindow) w); } w.updateFromUIDL(childUidl, client); } } // Close old windows which where not in UIDL anymore for (final Iterator rem = removedSubWindows.iterator(); rem .hasNext();) { final VWindow w = rem.next(); client.unregisterPaintable(w); subWindows.remove(w); w.hide(); } if (uidl.hasAttribute("focused")) { // set focused component when render phase is finished Scheduler.get().scheduleDeferred(new Command() { public void execute() { final Paintable toBeFocused = uidl.getPaintableAttribute( "focused", connection); /* * Two types of Widgets can be focused, either implementing * GWT HasFocus of a thinner Vaadin specific Focusable * interface. */ if (toBeFocused instanceof com.google.gwt.user.client.ui.Focusable) { final com.google.gwt.user.client.ui.Focusable toBeFocusedWidget = (com.google.gwt.user.client.ui.Focusable) toBeFocused; toBeFocusedWidget.setFocus(true); } else if (toBeFocused instanceof Focusable) { ((Focusable) toBeFocused).focus(); } else { VConsole.log("Could not focus component"); } } }); } // Add window listeners on first paint, to prevent premature // variablechanges if (firstPaint) { Window.addWindowClosingHandler(this); Window.addResizeHandler(this); } triggerSizeChangeCheck(); // finally set scroll position from UIDL if (uidl.hasVariable("scrollTop")) { scrollable = true; scrollTop = uidl.getIntVariable("scrollTop"); DOM.setElementPropertyInt(getElement(), "scrollTop", scrollTop); scrollLeft = uidl.getIntVariable("scrollLeft"); DOM.setElementPropertyInt(getElement(), "scrollLeft", scrollLeft); } else { scrollable = false; } // Safari workaround must be run after scrollTop is updated as it sets // scrollTop using a deferred command. if (BrowserInfo.get().isSafari()) { Util.runWebkitOverflowAutoFix(getElement()); } scrollIntoView(uidl); rendering = false; } private void updateLiferayPortletBodySize(UIDL childUidl) { // Liferay #5521: update portlet-body element size Element parentOfVApp = getElement().getParentElement() .getParentElement(); if (parentOfVApp != null && parentOfVApp.getClassName().contains("portlet-body")) { // portlet-body needs to get size 100% if e.g. in a freeform // layout or otherwise in a situation where the application // layout size is specified as a percentage for (String direction : new String[] { "height", "width" }) { if (childUidl.hasAttribute(direction) && childUidl.getStringAttribute(direction) .contains("%")) { // if layout has a percentual size, set portlet body size // as percentual if ("".equals(parentOfVApp.getStyle() .getProperty(direction))) { parentOfVApp.getStyle().setProperty(direction, "100%"); } } else if (parentOfVApp.getStyle().getProperty(direction) .contains("%")) { // if layout size is undefined or fixed and we have set a // percentual size for portlet-body, remove size setting on // portlet body parentOfVApp.getStyle().setProperty(direction, ""); } } } } /** * Tries to scroll paintable referenced from given UIDL snippet to be * visible. * * @param uidl */ void scrollIntoView(final UIDL uidl) { if (uidl.hasAttribute("scrollTo")) { Scheduler.get().scheduleDeferred(new Command() { public void execute() { final Paintable paintable = uidl.getPaintableAttribute( "scrollTo", connection); ((Widget) paintable).getElement().scrollIntoView(); } }); } } @Override public void onBrowserEvent(Event event) { super.onBrowserEvent(event); int type = DOM.eventGetType(event); if (type == Event.ONKEYDOWN && actionHandler != null) { actionHandler.handleKeyboardEvent(event); return; } else if (scrollable && type == Event.ONSCROLL) { updateScrollPosition(); } } /** * Updates scroll position from DOM and saves variables to server. */ private void updateScrollPosition() { int oldTop = scrollTop; int oldLeft = scrollLeft; scrollTop = DOM.getElementPropertyInt(getElement(), "scrollTop"); scrollLeft = DOM.getElementPropertyInt(getElement(), "scrollLeft"); if (connection != null && !rendering) { if (oldTop != scrollTop) { connection.updateVariable(id, "scrollTop", scrollTop, false); } if (oldLeft != scrollLeft) { connection.updateVariable(id, "scrollLeft", scrollLeft, false); } } } /* * (non-Javadoc) * * @see * com.google.gwt.event.logical.shared.ResizeHandler#onResize(com.google * .gwt.event.logical.shared.ResizeEvent) */ 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() { /* * IE (pre IE9 at least) will give us some false resize events due to * problems with scrollbars. Firefox 3 might also produce some extra * events. We postpone both the re-layouting and the server side event * for a while to deal with these issues. * * We may also 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 || (BrowserInfo.get().isIE() && BrowserInfo.get() .getIEVersion() <= 8) || BrowserInfo.get().isFF3(); if (lazy) { delayedResizeExecutor.trigger(); } else { performSizeCheck(); } } /** * Send new dimensions to the server. */ private void sendClientResized() { Element parentElement = getElement().getParentElement(); int viewHeight = parentElement.getClientHeight(); int viewWidth = parentElement.getClientWidth(); connection.updateVariable(id, "height", viewHeight, false); connection.updateVariable(id, "width", viewWidth, false); int windowWidth = Window.getClientWidth(); int windowHeight = Window.getClientHeight(); connection.updateVariable(id, BROWSER_WIDTH_VAR, windowWidth, false); connection.updateVariable(id, BROWSER_HEIGHT_VAR, windowHeight, immediate); } public native static void goTo(String url) /*-{ $wnd.location = url; }-*/; public void onWindowClosing(Window.ClosingEvent event) { // Change focus on this window in order to ensure that all state is // collected from textfields // TODO this is a naive hack, that only works with text fields and may // cause some odd issues. Should be replaced with a decent solution, see // also related BeforeShortcutActionListener interface. Same interface // might be usable here. VTextField.flushChangesFromFocusedTextField(); // Send the closing state to server connection.updateVariable(id, "close", true, false); connection.sendPendingVariableChangesSync(); } private final RenderSpace myRenderSpace = new RenderSpace() { private int excessHeight = -1; private int excessWidth = -1; @Override public int getHeight() { return getElement().getOffsetHeight() - getExcessHeight(); } private int getExcessHeight() { if (excessHeight < 0) { detectExcessSize(); } return excessHeight; } private void detectExcessSize() { // TODO define that iview cannot be themed and decorations should // get to parent element, then get rid of this expensive and error // prone function final String overflow = getElement().getStyle().getProperty( "overflow"); getElement().getStyle().setProperty("overflow", "hidden"); if (BrowserInfo.get().isIE() && getElement().getPropertyInt("clientWidth") == 0) { // can't detect possibly themed border/padding width in some // situations (with some layout configurations), use empty div // to measure width properly DivElement div = Document.get().createDivElement(); div.setInnerHTML(" "); div.getStyle().setProperty("overflow", "hidden"); div.getStyle().setProperty("height", "1px"); getElement().appendChild(div); excessWidth = getElement().getOffsetWidth() - div.getOffsetWidth(); getElement().removeChild(div); } else { excessWidth = getElement().getOffsetWidth() - getElement().getPropertyInt("clientWidth"); } excessHeight = getElement().getOffsetHeight() - getElement().getPropertyInt("clientHeight"); getElement().getStyle().setProperty("overflow", overflow); } @Override public int getWidth() { int w = getRealWidth(); if (w < 10 && BrowserInfo.get().isIE7()) { // Overcome an IE7 bug #3295 Util.shakeBodyElement(); w = getRealWidth(); } return w; } private int getRealWidth() { if (connection.getConfiguration().isStandalone()) { return getElement().getOffsetWidth() - getExcessWidth(); } // If not running standalone, there might be multiple Vaadin apps // that won't shrink with the browser window as the components have // calculated widths (#3125) // Find all Vaadin applications on the page ArrayList vaadinApps = new ArrayList(); loadAppIdListFromDOM(vaadinApps); // Store original styles here so they can be restored ArrayList originalDisplays = new ArrayList( vaadinApps.size()); String ownAppId = connection.getConfiguration().getRootPanelId(); // Hiding elements causes browser to forget scroll position -> must // save values and restore when the elements are visible again #7976 int originalScrollTop = Window.getScrollTop(); int originalScrollLeft = Window.getScrollLeft(); // Hiding elements causes IE8 to forget the focused element com.google.gwt.user.client.Element focusedElement = Util.getIEFocusedElement(); /* * As described in #12336, removing applications will decrease the * BODY height in such a way that you might temporarily lose the * scrollbar. As we hide the applications, we need to conserve the * original height of the applications, so that this can be taken * into account. */ int heightOfApplications = 0; // Set display: none for all Vaadin apps for (int i = 0; i < vaadinApps.size(); i++) { String appId = vaadinApps.get(i); Element targetElement; if (appId.equals(ownAppId)) { // Only hide the contents of current application targetElement = ((Widget) layout).getElement(); } else { // Hide everything for other applications targetElement = Document.get().getElementById(appId); } heightOfApplications += targetElement.getOffsetHeight(); Style layoutStyle = targetElement.getStyle(); originalDisplays.add(i, layoutStyle.getDisplay()); layoutStyle.setDisplay(Display.NONE); } /* * before measuring the width, let's make sure the body height is * still the same (to account for any possible scrollbars). */ com.google.gwt.user.client.Element expander = DOM .createElement("div"); expander.getStyle().setHeight(heightOfApplications, Unit.PX); BodyElement body = Document.get().getBody(); body.appendChild(expander); int w = getElement().getOffsetWidth() - getExcessWidth(); expander.removeFromParent(); // Then restore the old display style before returning for (int i = 0; i < vaadinApps.size(); i++) { String appId = vaadinApps.get(i); Element targetElement; if (appId.equals(ownAppId)) { targetElement = ((Widget) layout).getElement(); } else { targetElement = Document.get().getElementById(appId); } Style layoutStyle = targetElement.getStyle(); String originalDisplay = originalDisplays.get(i); if (originalDisplay.length() == 0) { layoutStyle.clearDisplay(); } else { layoutStyle.setProperty("display", originalDisplay); } } // Scroll back to original location Window.scrollTo(originalScrollLeft, originalScrollTop); // In IE8 restore focus: if(BrowserInfo.get().isIE8()) { Util.setIEFocusedElement(focusedElement); } return w; } private int getExcessWidth() { if (excessWidth < 0) { detectExcessSize(); } return excessWidth; } @Override public int getScrollbarSize() { return Util.getNativeScrollbarSize(); } }; private native static void loadAppIdListFromDOM(ArrayList list) /*-{ var j; for(j in $wnd.vaadin.vaadinConfigurations) { // $entry not needed as function is not exported list.@java.util.Collection::add(Ljava/lang/Object;)(j); } }-*/; public RenderSpace getAllocatedSpace(Widget child) { return myRenderSpace; } public boolean hasChildComponent(Widget component) { return (component != null && component == layout); } public void replaceChildComponent(Widget oldComponent, Widget newComponent) { // TODO This is untested as no layouts require this if (oldComponent != layout) { return; } setWidget(newComponent); layout = (Paintable) newComponent; } public boolean requestLayout(Set child) { /* * Can never propagate further and we do not want need to re-layout the * layout which has caused this request. */ updateParentFrameSize(); // layout size change may affect its available space (scrollbars) connection.handleComponentRelativeSize((Widget) layout); return true; } private void updateParentFrameSize() { if (parentFrame == null) { return; } int childHeight = Util.getRequiredHeight(getWidget().getElement()); int childWidth = Util.getRequiredWidth(getWidget().getElement()); parentFrame.getStyle().setPropertyPx("width", childWidth); parentFrame.getStyle().setPropertyPx("height", childHeight); } private static native Element getParentFrame() /*-{ try { var frameElement = $wnd.frameElement; if (frameElement == null) { return null; } if (frameElement.getAttribute("autoResize") == "true") { return frameElement; } } catch (e) { } return null; }-*/; public void updateCaption(Paintable component, UIDL uidl) { // NOP Subwindows never draw caption for their first child (layout) } /** * Return an iterator for current subwindows. This method is meant for * testing purposes only. * * @return */ public ArrayList getSubWindowList() { ArrayList windows = new ArrayList(subWindows.size()); for (VWindow widget : subWindows) { windows.add(widget); } return windows; } public void init(String rootPanelId, ApplicationConnection applicationConnection) { DOM.sinkEvents(getElement(), Event.ONKEYDOWN | Event.ONSCROLL); // iview is focused when created so element needs tabIndex // 1 due 0 is at the end of natural tabbing order DOM.setElementProperty(getElement(), "tabIndex", "1"); RootPanel root = RootPanel.get(rootPanelId); // Remove the v-app-loading or any splash screen added inside the div by // the user root.getElement().setInnerHTML(""); // For backwards compatibility with static index pages only. // No longer added by AbstractApplicationServlet/Portlet root.removeStyleName("v-app-loading"); root.add(this); if (applicationConnection.getConfiguration().isStandalone()) { // set focus to iview element by default to listen possible keyboard // shortcuts. For embedded applications this is unacceptable as we // don't want to steal focus from the main page nor we don't want // side-effects from focusing (scrollIntoView). getElement().focus(); } parentFrame = getParentFrame(); } public ShortcutActionHandler getShortcutActionHandler() { return actionHandler; } public void focus() { getElement().focus(); } }