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

com.google.gwt.user.client.ui.RootPanel Maven / Gradle / Ivy

/*
 * Copyright 2008 Google Inc.
 * 
 * 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.google.gwt.user.client.ui;

import com.google.gwt.dom.client.BodyElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.i18n.client.BidiUtils;
import com.google.gwt.i18n.client.HasDirection;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * The panel to which all other widgets must ultimately be added. RootPanels are
 * never created directly. Rather, they are accessed via {@link RootPanel#get()}
 * .
 * 
 * 

* Most applications will add widgets to the default root panel in their * {@link com.google.gwt.core.client.EntryPoint#onModuleLoad} methods. *

*/ public class RootPanel extends AbsolutePanel { /** * A default RootPanel implementation that wraps the body element. */ private static class DefaultRootPanel extends RootPanel { public DefaultRootPanel() { super(getBodyElement()); } @Override protected void setWidgetPositionImpl(Widget w, int left, int top) { // Account for the difference between absolute position and the // body's positioning context. left -= Document.get().getBodyOffsetLeft(); top -= Document.get().getBodyOffsetTop(); super.setWidgetPositionImpl(w, left, top); } } /** * The singleton command used to detach widgets. */ private static final AttachDetachException.Command maybeDetachCommand = new AttachDetachException.Command() { public void execute(Widget w) { if (w.isAttached()) { w.onDetach(); } } }; private static Map rootPanels = new HashMap(); private static Set widgetsToDetach = new HashSet(); /** * Marks a widget as detached and removes it from the detach list. * *

* If an element belonging to a widget originally passed to * {@link #detachOnWindowClose(Widget)} has been removed from the document, * calling this method will cause it to be marked as detached immediately. * Failure to do so will keep the widget from being garbage collected until * the page is unloaded. *

* *

* This method may only be called per widget, and only for widgets that were * originally passed to {@link #detachOnWindowClose(Widget)}. *

* * @param widget the widget that no longer needs to be cleaned up when the * page closes * @see #detachOnWindowClose(Widget) */ public static void detachNow(Widget widget) { assert widgetsToDetach.contains(widget) : "detachNow() called on a widget " + "not currently in the detach list"; try { widget.onDetach(); } finally { widgetsToDetach.remove(widget); } } /** * Adds a widget to the detach list. This is the list of widgets to be * detached when the page unloads. * *

* This method must be called for all widgets that have no parent widgets. * These are most commonly {@link RootPanel RootPanels}, but can also be any * widget used to wrap an existing element on the page. Failing to do this may * cause these widgets to leak memory. This method is called automatically by * widgets' wrap methods (e.g. * {@link Button#wrap(com.google.gwt.dom.client.Element)}). *

* *

* This method may not be called on any widget whose element is * contained in another widget. This is to ensure that the DOM and Widget * hierarchies cannot get into an inconsistent state. *

* * @param widget the widget to be cleaned up when the page closes * @see #detachNow(Widget) */ public static void detachOnWindowClose(Widget widget) { assert !widgetsToDetach.contains(widget) : "detachOnUnload() called twice " + "for the same widget"; assert !isElementChildOfWidget(widget.getElement()) : "A widget that has " + "an existing parent widget may not be added to the detach list"; widgetsToDetach.add(widget); } /** * Gets the default root panel. This panel wraps the body of the browser's * document. This root panel can contain any number of widgets, which will be * laid out in their natural HTML ordering. Many applications, however, will * add a single panel to the RootPanel to provide more structure. * * @return the default RootPanel */ public static RootPanel get() { return get(null); } /** * Gets the root panel associated with a given browser element. For this to * work, the HTML document into which the application is loaded must have * specified an element with the given id. * * @param id the id of the element to be wrapped with a root panel ( * null specifies the default instance, which wraps the * <body> element) * @return the root panel, or null if no such element was found */ public static RootPanel get(String id) { // See if this RootPanel is already created. RootPanel rp = rootPanels.get(id); // Find the element that this RootPanel will wrap. Element elem = null; if (id != null) { // Return null if the id is specified, but no element is found. if (null == (elem = Document.get().getElementById(id))) { return null; } } if (rp != null) { // If the element associated with an existing RootPanel has been replaced // for any reason, return a new RootPanel rather than the existing one ( // see issue 1937). if ((elem == null) || (rp.getElement() == elem)) { // There's already an existing RootPanel for this element. Return it. return rp; } } // Note that the code in this if block only happens once - // on the first RootPanel.get(String) or RootPanel.get() // call. if (rootPanels.size() == 0) { hookWindowClosing(); // If we're in a RTL locale, set the RTL directionality // on the entire document. if (LocaleInfo.getCurrentLocale().isRTL()) { BidiUtils.setDirectionOnElement(getRootElement(), HasDirection.Direction.RTL); } } // Create the panel and put it in the map. if (elem == null) { // 'null' means use document's body element. rp = new DefaultRootPanel(); } else { // Otherwise, wrap the existing element. rp = new RootPanel(elem); } rootPanels.put(id, rp); detachOnWindowClose(rp); return rp; } /** * Convenience method for getting the document's body element. * * @return the document's body element */ public static native com.google.gwt.user.client.Element getBodyElement() /*-{ return $doc.body; }-*/; /** * Determines whether the given widget is in the detach list. * * @param widget the widget to be checked * @return true if the widget is in the detach list */ public static boolean isInDetachList(Widget widget) { return widgetsToDetach.contains(widget); } // Package-protected for use by unit tests. Do not call this method directly. static void detachWidgets() { // When the window is closing, detach all widgets that need to be // cleaned up. This will cause all of their event listeners // to be unhooked, which will avoid potential memory leaks. try { AttachDetachException.tryCommand(widgetsToDetach, maybeDetachCommand); } finally { widgetsToDetach.clear(); // Clear the RootPanel cache, since we've "detached" all RootPanels at // this point. This would be pointless, since it only happens on unload, // but it is very helpful for unit tests, because it allows // RootPanel.get() to work properly even after a synthesized "unload". rootPanels.clear(); } } /** * Convenience method for getting the document's root () element. * * @return the document's root element */ private static native Element getRootElement() /*-{ return $doc; }-*/; private static void hookWindowClosing() { // Catch the window closing event. Window.addCloseHandler(new CloseHandler() { public void onClose(CloseEvent closeEvent) { detachWidgets(); } }); } /* * Checks to see whether the given element has any parent element that * belongs to a widget. This is not terribly efficient, and is thus only used * in an assertion. */ private static boolean isElementChildOfWidget(Element element) { // Walk up the DOM hierarchy, looking for any widget with an event listener // set. Though it is not dependable in the general case that a widget will // have set its element's event listener at all times, it *is* dependable // if the widget is attached. Which it will be in this case. element = element.getParentElement(); BodyElement body = Document.get().getBody(); while ((element != null) && (body != element)) { if (Event.getEventListener(element) != null) { return true; } element = element.getParentElement().cast(); } return false; } private RootPanel(Element elem) { super(elem); onAttach(); } /** * Clears the rootPanel. If clearDom is true, then also remove any DOM * elements that are not widgets. * *

By default {@link #clear()} will only remove children that are GWT widgets. * This method also provides the option to remove all children including the * non-widget DOM elements that are directly added (e.g. elements added via * {@code getElement().appendChild(...)}. * * @param clearDom if {@code true} this method will also remove any DOM * elements that are not widgets. * * @deprecated Simply removing all DOM elements can cause issues with other elements in the page. */ @Deprecated public void clear(boolean clearDom) { clear(); if (clearDom) { getElement().removeAllChildren(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy